Transcript
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
>> Hello, everyone.
I assume you guys are all here
because you care
about battery life.
And if you don't, well, I
got your bosses to thank
because they're probably the
ones that sent you here today.
Well, either way, thank
you so much for coming
to the second half of
Writing Energy Efficient Code.
My name is Albert.
I'm an engineer on the
iOS Software Power team.
And today, I want to talk
about how to apply some
of the energy concepts and
principles that, you know,
was discussed in
the first session
to the various different
technologies
and features you might be
using in your application.
So, last year we introduced the
energy menu in OS X Mavericks,
and this year we're introducing
the Battery Usage Menu in iOS.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and this year we're introducing
the Battery Usage Menu in iOS.
Now, this menu aims to break
down your iOS device's energy
and battery use by application.
Both of those menus will
help educate as well
as provide our users
with information
on where their battery has gone
in their devices because we know
that our users care
about battery life.
According to surveys,
one of the top reasons
that would prompt
someone to spend money
to buy a new phone is
better battery life.
So, our users do care about the
battery life of their devices.
And with both of these
menus, our message is clear.
We, at Apple, also care
about battery life.
We go to great lengths
and efforts to ensure
that our laptops and
mobile devices have some
of the best battery life
in the industry and we do
that because we know that great
battery life is a key part
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of great user experience.
Now, as a developer myself,
I know that oftentimes
schedules are tight, right?
Requirements are always
changing and getting features
and functionalities to
work is your top priority.
However, what I hoped
to show you today is
that by keeping energy
in mind, as you design
and architecture applications,
you might actually be able
to simplify your
development cycles.
I hope that you will be able
to walk away with APIs or ideas
on how you can minimize
and reduce the energy use
of your applications, and
really, my goal today is
to help you prevent your
application from going
up to the top of our
battery list unnecessarily.
So, through the next hour,
we'll go through a quick recap
of what Anthony has
discussed in the first session
and then we'll talk
about how to apply some
of those principles
to networking.
After that, I want to show you
how you can actually measure the
energy usage of your application
through some tools that we have,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
energy usage of your application
through some tools that we have,
and then we'll camp
on the idea of sleep
for the rest of our session.
So with that, let's begin our
session with a quick recap.
So, in the first session,
for those of you guys
who weren't able to attend,
Anthony briefly talked
about two energy concepts
that really anchor
energy efficient coding.
First is the idea of
fixed cost or what I would
like to simply refer to
as the overhead cost.
Resources on our system, by
default, stay in idle state
and this is to conserve power.
Now, when your app
requests to do some work,
the system will need to
power up that resource.
We need to keep the resource up
while you're doing your work.
And then most resources go
through an intermediate state
where the resources are kept up
in case some other work needs
to be done very shortly.
Now this is so that you
won't have to pay or wait
for the resource to boot
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
for the resource to boot
up again while you're
doing-while you're doing
your work.
But the system does stay up a
little bit-the resources stay
up a little bit even after
you finish your work.
And finally, when
the resource decides
that there's no more
work to be done,
it'll go back down
to idle state.
So, as you can see on the screen
here, the area in the green is,
you know, the work that you're
doing in your application,
but the area under in
blue is the fixed cost
or the overhead cost.
And there is these
intermediate stanzas
between where the
resource is still kept
up while you're not
doing any work.
So, if you want to
minimize the energy used
for your applications, you'll
want to do as much work
as you can while you
do have the resource
so you can minimize the overhead
cost of doing your work.
Second is the idea of
trading power for energy.
Now, a lot of resources on our
systems have different power
and performance states
and that's
to conserve power
while still ensuring
that we can provide
fluid performances.
We all know, hopefully,
that energy is a factor
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
We all know, hopefully,
that energy is a factor
of both the power used as
well as the time it takes
for you to finish your work.
As you can see on the screen
on the figure on your right,
the idea here is that
by doing as much work
as you can-although, you could
potentially be using a lot more
power instantaneously-you'll
be able
to finish your work quicker,
minimizing the overhead cost
as well as the overall
energy used.
Essentially, you're trading
power for time to reduce energy.
So, that's trading
power for energy.
Now, as developers, I know
that these concepts can be kind
of abstract and hard to apply
when you're actually coding.
So, Anthony boiled
it down for you guys.
Here's what reducing energy use
really comes down to and that's
to do it never, to do
it at a better time,
to do it more efficiently
and to do it less.
You'll want to do work never,
do work at a better time,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
do work more efficiently
and do work less.
That's what reducing
energy really comes down to.
Now, Anthony went into
great detail on how
to apply these principles to
minimizing the system activity
of your applications in
reducing-essentially,
reducing the CPU usage.
So, let me just quickly go
over that for you real quick.
For doing it never, Anthony
talked about reacting
to app transitions, right?
If your application is
going into the background
or is not going to
be the frontmost app,
make sure you stop
any unnecessary work,
such as any type of rendering
because your users
will not see it.
Now, for doing it at a
better time, Anthony talked
about two scheduling APIs
that you would consider using
and that's the
NSBackgroundActivityScheduler
for OS X and the NSURLSession
for both iOS and OS X
to schedule your
network transactions.
Now, we'll talk
about NSURLSession a
little bit more later.
For doing it more
efficiently, Anthony talked
about the new quality of
service APIs and he really went
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
about the new quality of
service APIs and he really went
into detail on how you can
set the appropriate quality
of servers with the priority of
your NSOperation or GCD queues
so you can do your
work more efficiently.
And finally, for doing it less,
Anthony mentioned the importance
of coalescing your
timers, right?
You want to coalesce your
timers to let your system,
your CPU idle as
much as you can.
Now, before I continue, I wanted
to take a quick detour and talk
about something new for iOS.
Now, up until now, we've
seen many developer bugs
where their applications
that are running
in the background are
utilizing a lot of CPU
for an extended period of time.
This should be considered bugs
because there are pretty
clear guidelines on why
and when an iOS app can
run in the background.
So, as you help developers
catch these bugs,
as well as minimize the
battery impact on the device
when this happens, we've
implemented a CPU monitor
that will kick in when your
applications are running-are
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that will kick in when your
applications are running-are
considered background running.
Now, what this will mean for
your applications is-well,
it really shouldn't
mean anything, right?
The CPU limits that we've
set are purely meant
to catch runaway usage.
They're set at a limit where
if you're doing the normal type
of work that you've
requested according
to the background modes that
you have in your applications,
you should not be
affected in any way.
However, if you do end
up hitting the CPU limit,
your app will be terminated
and a log will be generated
to help you identify
that this has happened.
Here's what this
log will look like.
So, up top, you'll
see that the way
to identify is the exception
type, which is EXC-RESOURCE
and the subtype,
you'll see CPU-FATAL.
This is how you'll know
that your application
that was running in the
background was terminated.
And on the bottom, we've
included some microstackshots
of what your application
was doing right before we
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of what your application
was doing right before we
terminated it.
This should help you
identify in your CodePath
where you had your runaway CPU
usage so that you can fix it.
So this is really-now, this will
not only preserve battery life
but it will help our developers,
which is you guys, to catch bugs
that you might not have known.
So once again, this is new
and we'll begin in iOS 8.
So, let me go back to
the recap-and I wanted
to mention one last thing
that Anthony talked about
and that's really on
energy-an energy efficient way
to do graphics and animation.
And the two points that
he talked about were
to avoid extraneous
screen updates, right?
Don't be updating the screen
when it's not necessary
and to review the
blur effect usage.
Now, blur is a great way to
include depth as well as,
you know, to provide
layering effect for your UIs.
However, with all great
things, they can cost more.
So, you'll want to review,
you know, all the animations
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So, you'll want to review,
you know, all the animations
or frame changes that you're
doing behind your blur and try
to minimize them
as much as possible
so that you can provide
your users
with a great UI while
helping them conserve their
battery life.
So, that pretty much
sums up our recap.
If you have any questions
about what I just, you know,
breezed through,
feel free to swing
by our labs this afternoon.
You know, once again,
we have one at 2 o'clock
in the Core OS Lab
and tomorrow at 3:15.
Anthony, myself, as well as
our colleagues, will be happy
to answer any questions that you
may have or check out the video
when it becomes available.
Anthony really goes into great
detail on all these things
that I just, you know,
quickly talked about.
So, to begin our session
today, I want to start
by talking about networking.
We live in a time where
the way we get information,
the way we shop, the way
we connect with friends,
or the way we backup important
data, or, you know, memories,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
or the way we backup important
data, or, you know, memories,
is really through networking.
I go as far as to say that
there's probably very few apps
in the App Store that
don't do any networking
and so it's important that we
discuss how we can do networking
in an energy efficient way.
So, let's start with a
very generic example.
Let's say today your requirement
is to develop an application
to send some data
to your servers.
So, an easy way to do this
and the most intuitive way
to do this would be, right when
data comes we'll, you know,
go ahead and send it off
to the servers, right?
So, as you can see as data is
coming in, we're firing up,
we're setting up connections,
sending into our servers.
Let's see what the energy cost
looks like for doing this.
So, here you have a couple of
data syncs coming in, right?
And let's assume that the data
that you're sending is small
so you think to yourself, "Well,
I'm just, you know, sending,
like, small packets up to our
server every once in a while.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
like, small packets up to our
server every once in a while.
It's probably not going to
use that much energy, right?"
Well, given what we just
talked about in overhead costs,
by default, the radios
on our system are idle
and the radios are the resources
that you're going to utilize
to send your network, to send
your packets to your servers.
So, in order to send these data
packets, the system has to bring
up the radios, keep the radios
up while you're doing your work.
And then, if you take a look
at the transfers happening
to your left, you'll see
that, you know, like we said,
even though there are times
where you're not actually
sending the packets out,
the radios are actually kept up.
This is the overhead cost
that we talked about earlier.
And then, if you look at
the transfer on your right,
even though you're only
sending one small data packet,
you're still paying the
cost to bring the radios
up for you to do that, right?
So, there's a big overhead
cost in using the radios.
Now, not to complicate
things, but depending
on which technology you're
using to do your transfers,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
on which technology you're
using to do your transfers,
the cost of both
the time out as well
as the energy used
can vary greatly.
All right, to give
you guys an idea,
if you're simply web browsing
constantly on an iPhone 5S,
if you're doing it
through Wi-Fi,
your battery will
last about 10 hours.
However, if you're
doing this through 3G,
it will only last you about 8.
So, the technology you utilize
to do your transfers will
affect the energy used
in your network transactions,
right, whether you're on Wi-Fi,
or cellular, or even, you know,
different cellular carriers kind
of impacts the energy you
use through your networking.
Not only that, your network
condition, your signal strength
as well as your network
throughput can impact the time
it takes as well as the
energy we use to send off
to your network transactions.
Networking can be
quite expensive
if not done intelligently.
So, let's go back
and take a look
at our simple web server
example, and this time instead
of sending in one at a time,
let's implement a simple buffer.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of sending in one at a time,
let's implement a simple buffer.
So, let's buffer the data on
our device before we send it.
Now, instead of paying the
overhead cost to send each
of those packets, your energy
will look something more
like this.
As you can see while
you're accumulating data,
your radio-the radios
get to stay idle
and so you're not
paying the overhead cost.
And once you do need to
send a group of data,
although you do still need
to bring up the radios,
it's not-it's a lot more
worth it now, right?
You're sending a lot more
data while paying less
of an overhead cost
or actually-sorry,
you're sending the
same amount of data
but paying less overhead cost.
So, it's important to know
that the way you schedule your
network operations can greatly
impact the energy use
in your application.
So, let's think about
how we can apply some
of the principles Anthony
talked about earlier
to the networking you're
doing in your application
to help reduce this energy.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to help reduce this energy.
So, for doing it more
efficiently, we just looked
at the importance of coalescing
your transactions, right?
By doing more at once, you're
paying less of an overhead cost
and you're reducing
the energy used
by sending the same
amount of packets.
Now, for doing it less
and never, it's simple.
Cut down your transfers, right?
The best way to conserve
energy is to do less work.
So think about you-think
about creative ways
that you can reduce the data
sizes in your application.
If you're sending media-like,
let's say you're uploading a
photo to a social media website.
And, you know, if the photo
is never going to be displayed
at its full quality, consider
reducing the image quality first
before you send it
out to your servers.
Compression is also
another easy way.
If you're sending files,
compress that file before you
send it up to your servers
to reduce the amount of
data that you're sending.
Now, on top of reducing
data sizes,
make sure you think about-make
sure you try to avoid,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
make sure you think about-make
sure you try to avoid,
as much as you can,
redundant transfers, right?
And make sure you
cache information
that you've already downloaded.
This will not only
help you save energy
but it can also improve
the performance
of your application, right?
Since the next time you need an
asset, you don't have to wait
for it to be downloaded again.
It will already be
readily available.
Also, use resumable
transactions.
Especially on mobile devices,
network connections can go in
and out fairly frequent,
so you want to make sure
you're using transactions
that can be paused and resumed.
So, if you detect the network
condition is bad, you can pause,
and you don't have to
resend those chunks of data
that was lost when you
do get to send them.
Now, finally, with networking
conditions, you won't-you want
to make sure that you handle
your network errors properly,
right?
And make sure you have good
timeout or retry policies.
It does not make any sense
for you to continuously try
to send data to your
servers when you can't.
So, these are just some
of the things to consider
on how you can reduce
the data sizes
of your network transactions.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of your network transactions.
Finally, for doing
it at a better time,
we have consider tolerance.
Basically, you want
to understand the requirements
of your application.
Is this network-does this
networking transaction need
to happen immediately, right?
As we've just looked at earlier,
the technology we use as well
as the signal conditions can
greatly impact the energy use
of your application when you're
doing those transactions.
So, if the-if the
conditions aren't ideal,
can we wait on this
network transaction?
You know, this is especially
important for larger downloads
such as, let's say, you're
backing up a user's data or,
you know, you're
downloading a movie
that probably is just
purely meant to be put
in the user's media library.
Those things won't have
an immediate user impact
and so consider doing
those at a better time.
And we'll talk about
NSURLSession more later
on an easy way to do this,
but let's take a look back
at our example first to
see how we can apply some
of these principles to
help reduce the energy
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of these principles to
help reduce the energy
of your network.
So, we already determined
that by buffering your data,
you greatly reduce
the overhead cost.
Now, let's implement a simple
check on the device to make sure
that the data is
actually relevant.
Let's say you simply diff the
current data versus the previous
to make sure that something has
actually changed before you send
it off, right?
Now, I have been able to reduce
the data size of my transfer
and finally, let's make sure
that the signal conditions are
good before we send it off,
you know?
And then, obviously,
here I'm assuming
that this data is not
needed immediately,
so there is some
tolerance before I need
to do this transaction.
Now, by implementing these
three things, I've been able
to save a lot of energy while
still providing the data
that needs to be provided.
So by now, I mean, a lot of you
guys are probably asking like,
"This is not easy at all, right?
Especially with signal
conditions,
especially with retries-like,
this is not an easy thing
to implement and it kind
of goes way over my head."
Well, this is where I'll
talk about NSURLSession.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Well, this is where I'll
talk about NSURLSession.
This is an API that will allow
you to easily integrate pause
and resume as well as implement
caching with NSURLCaching.
Now, one of the good
features in this API is,
as Anthony has mentioned
earlier,
the ability to create
background sessions.
So, with background
sessions, you're actually able
to do your downloads
out of process.
And this is especially
important for iOS apps
because your application won't
get to run all the time, right?
So with background sessions,
you no longer have to rush
to download everything
you want immediately.
You just simply-you can
create a background session
and allow the system
to do that for you
and your app will be alerted
whenever the transactions are
done or if it errors out.
Now, within iOS, when your
applications are moved
to the background,
these background sessions will
actually also automatically
opt-in to throughput monitoring
as well as automatic retries.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
opt-in to throughput monitoring
as well as automatic retries.
So, those are some things
that will be automatically
taken care of for you in iOS.
Now, one of the features that
Anthony mentioned, and I'm going
to once again stress the
importance of-for you
to consider using this as the
discretionary flag, right?
You can set for your
background sessions,
you can set it to
be discretionary.
And by doing this, you're
basically telling the system,
this is one of those
"let's do it
at a better time"
type transactions.
The system will automatically
know
that this isn't needed
immediately
and find the most
power optimal time
to schedule your
network transactions.
And all of this is transparent
to your app, so you don't need
to know anything about it.
You'll simply get alerted
when the transaction is done
or if it errors out, right?
Anthony also mentioned
that you can adjust the
scheduling window.
You can give the system
a certain amount of time
that you want this
transaction to occur.
And, generally, you want to
set it to be over 12 hours
to allow the system to actually
be able to schedule it since,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to allow the system to actually
be able to schedule it since,
you know, networking
connections can go in and out
and the user can be
using their devices.
But, you know, make sure you
set this to over 12 hours
and the system will do its best
to schedule your
network transaction
within this time window.
Now, if it is unable to,
an error will be thrown
and then you can
react accordingly.
For instance, you
know, immediately try
to do your networking
transactions or, you know,
once again schedule another one,
giving, you know, another window
for you to wait if this
transaction can happen
at a later time.
So, here is a code snippet
of what this may look
like in your application and
it's quite simple, right?
All you have to do is make sure
you set up your configuration.
So, here I have a background
session configuration setup.
And, you know, here
I can set some flags.
For instance, I only want to do
this when users are connected
to Wi-Fi because I know that
that's more power efficient.
And also I want to
save my user's money
because obviously
data costs money.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Then we set our discretionary
flag, right?
So this is where I tell
the system, "You take care
of downloading this at
whatever time you think is best.
Just tell me when it's done."
And then I say, "I want it
within the next 18 hours."
Now, you just have to
create your session
with the configuration
that we just created.
Create task and send it off.
That's it.
The system will now do all
the downloading for you
in an energy efficient
way and all
of this is transparent
to your application.
So, this is really
a great API for you
to do your networking
transactions
in an energy efficient way.
For more information, you
know, check out the "What's New
in Foundation Networking"
video when it becomes available
or swing by the networking labs.
But let's sum up
networking, right?
Essentially, networking
comes down to these three Ts,
all right-to coalesce
your transactions,
to cutdown your transfers, and
to consider tolerance, right?
So keep these three
things in mind
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So keep these three
things in mind
as you are designing
how you're going
to schedule your
networking operations
to minimize the energy
use of networking.
So, we talked a lot about
different techniques and,
you know, APIs for
you to be able
to reduce the energy
use of your application.
How do you know that you've
actually achieved that
or how can you actually measure
and profile the energy
use of your application?
I hope that's the next
question that you've thought
of because that's what
we're going to talk about.
So, on for OS X developers,
you could do this directly
through Xcode, right?
In the Xcode debugger,
as you can see,
there is this little
energy section here
that can really help
you kind of understand,
at least to some
extent, the energy impact
of your application
as it's running.
For iOS developers,
however, you will not be able
to use Xcode to do this.
What you will be able to do is
to pre-record a trace of-you
know, let's say you want
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to pre-record a trace of-you
know, let's say you want
to profile some activity,
you can record a trace
of the system's energy usage as
well as some other information,
and then you can use the
Energy Diagnostics tools
and instruments to help you
visualize what's going on.
To record this trace, simply
navigate to the Instruments menu
and this is in Settings
Developer menu.
You'll see an Instruments
menu that you can select
and simply click Energy.
And, you know, selectively
you can choose
to record networking information
as well and just hit Start.
Now the system will begin
recording your traces
and you can just simply navigate
through your application,
do whatever work you
wanted to do to profile it.
And then when you're done,
come back to this menu
and hit Stop Recording.
Now your traces are recorded.
I do want to mention there's
a little subtext right there.
I don't know if those of you
in the back can see this,
but basically, we'll only
save one trace at a time.
So, if you click
Start Recording again,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So, if you click
Start Recording again,
your previous trace
will be erased.
So, keep that in mind.
Now, you have your
traces recorded.
Simply connect your
device, you know,
to your Macs, to your laptops.
And then through the Instruments
Energy Diagnostics menu,
you just have to click
Import Data from device.
That's what that, you know,
highlighted blue selection is.
And then your traces will
be-you'll be able to pull
up your traces on instruments
with the Energy Diagnostics
tool.
So, I'm going to go ahead
and show you guys what this
may look like for an iOS app.
So, this should help
familiarize you guys
with the energy diagnostics
tool that we have to help you,
you know, kind of visualize
the energy impact of your apps.
So, as you can see here
I prerecorded two traces
that I'm going to
show, but let's focus
on this first one here.
So, up top of energy diagnostics
we have energy used, right?
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So, up top of energy diagnostics
we have energy used, right?
And this really tells
you the system energy
as your application was running.
And if you look at the
details page on the bottom,
it might be hard to see,
so, let me see if I can zoom
in on this-Great,there you go.
So, you'll see that
energy used is, you know,
really an instantaneous
reading based on one to 20.
Twenty means you're
using a lot of energy
and one means you're
using very little energy.
So, you can see as I've recorded
this trace about-for about,
let's see, 13 minutes-you'll
be able to see that energy is,
you know, generally pretty high.
What I'm doing here is
every about 30 seconds,
I'm firing off a network
connection, right?
Here to the Connections tab,
you'll see all your connections.
And you can actually even
see CPU activity, right?
So, CPU is fairly active since
every 30 seconds I'm firing
up network connection.
Now, to help you better debug,
you can also-you
also get information
such as network activity.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So, you'll be able
to see, you know,
how many bytes you've
transferred
from your application,
what technology was going
through and, you know-let's
see, as you can see here,
you have your cellular.
You have Wi-Fi.
All right.
Let's come back out.
And then, we also
give you information
such as Display Brightness.
Now Display is also an area
that can use up a lot of energy
so you're-so, if your
application is, you know,
pushing the display up all the
way to its full brightness,
make sure you disable that
feature when it's not needed.
But here my display is
about 20 percent brightness,
might-and we give you
sleep-Sleep/Wake information
for your iOS devices.
So, if your device was asleep,
we'll still be able to show
that here and it will
look something more
like the dark red area here.
And then there's
Bluetooth, Wi-Fi and GPS.
Now these three are
purely on an on and off.
We only show you if
it's on or it's off.
So, this is energy diagnostics.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So, this is energy diagnostics.
It's really a good way for
you to see the system energy,
what was going on in the system
when your application
is running.
Now, just to show
guys the importance
of coalescing your transactions,
I've recorded a second trace
where instead of every 30
seconds, as you can see here,
I'm only firing up
connection every about two
to three minutes
here-two minutes.
So, as you can see, you know,
it looks like I'm
sending a lot more data.
However, if you look at
the energy usage here,
and let's zoom in,
our previous trace,
our energy usage was pretty
much stuck around-let's see
if can get this to zoom
in...there you go-stuck
around 14, 15, 16,
throughout the whole trace.
However, simply by
coalescing my transactions,
now the energy usage of my
application, there you go,
you begin to see
some like sixes,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
you begin to see
some like sixes,
sevens, and eights, right?
And then when you are firing
your transactions, once again,
the energy usage will go
up to 14, you know, 16, 17,
but it comes back down again.
So, it is important to coalesce
your network transactions
and this is energy diagnostics.
It's really a great way for you
to visualize the energy
use of your application.
So, we're about half way through
our session and we talked
about energy efficient
networking, applying some
of the principles through
the networking you're doing
in your application.
We talked about how to
use energy diagnostics
to measure the energy
impact of your application
for iOS devices and iOS apps.
Next, I'm going to camp
on the idea of sleep.
Now, what I'm about to talk
about will be-the concepts
that I'm going to discuss can
be applied for OS X developers.
However, I just want to point
out that the technologies
as well as features I
will be discussing more
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
as well as features I
will be discussing more
in detail are primarily on iOS
devices, so keep that in mind.
Now, a wise man once said,
"To sleep is to prepare
for the longer journey ahead."
Now, that's true for you and me
and that's also true
for our devices.
The battery life of our
devices really depends on sleep.
Once again, to give you guys
an idea on our favorite 5
or 5S-iPhone 5S like we saw
earlier-if you're doing constant
web browsing, you know,
the screen is on, right?
The radios are up, you
know, you're downloading
and uploading packets, you know,
the display is-the display is
on like I said and you know
your device is fairly active
when you're doing these things.
So, you'll get about eight
to 10 hours of battery life.
However, let's say
instead of web browsing,
you're simply just playing
audio on your device.
All right now, the
screen is off,
the system is a lot
less, you know, busy.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
the system is a lot
less, you know, busy.
It's probably doing some audio
packet audio-it's probably
decoding some audio packets,
running those to your speakers,
but it's fairly idle,
so, you know,
we get about 40 hours
of battery life.
However, if your system
is completely idle
and asleep you can get about
250 hours of battery life.
That's like 10 days'
worth of battery life.
Now, I know it's not really
practical for you to just look
at this number because
our users are not going
to just buy a device, leave
it on a table, hope it sleeps
so they can get 10 days'
worth of battery life.
That's not really useful at all.
However, the concept
is simple here, right?
The longer you allow
your devices to sleep,
the better battery
life it will get.
So, for our typical user,
when they turn off the screen
of their device, they either
set it aside or put it
in their pockets, they assume
that the device is
asleep, right?
They're not interacting
with the device.
It's, you know, out
of their sight.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
It's, you know, out
of their sight.
The device should not
be doing any work.
However, as developers, we know
that that is not
the case, right?
When the device is
seemingly asleep,
your apps are often waking
up the device to interact
with all these different
technologies and features
to provide their users with
up-to-date information.
Now, this is necessary,
obviously,
because our users expect,
you know, their emails
to be coming through, our
users expect the latest news
to be there when they, you
know, start using their device.
However, you know,
every time you do work,
you can pretty much expect
the device needs to be awake
and thus, even though you're
only doing a little bit of work,
and here's our favorite chart
again, our favorite graph.
Even though you're only
doing a little bit of work,
you're pretty much paying
the whole overhead cost
of keeping the device awake.
And worst of all is all of
this is happening transparent
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And worst of all is all of
this is happening transparent
to our users.
So, if you're doing
a lot of work
in the background while
the-while seemingly the device
is asleep, when the user turns
on their device and wants
to use it again,
they'll be very confused
as to why their battery
has drained
and they will not
be happy customers.
So, it's especially
important for you
to minimize the energy
use of your applications
when they're running
in the background.
And, really, the best way to do
that is to minimize the number
of times your application has
to keep the device awake, right?
Do as much work as you can
so you minimize the number
of time you'll need
to be doing that work.
So, for the-with the
remaining time that we have,
I want to talk about these four
areas in Notifications, VoIP,
Location and Bluetooth.
I want to talk about some
of the best practices
as your applications are
utilizing these technologies
when it's running
in the background.
So, first, from notifications.
Notification is a great way
for your application to send,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Notification is a great way
for your application to send,
you know, to just alert the
users of some information.
Now, it is important for
you to know, however,
every time you either
schedule a local notification
or send a remote
push notification,
the device will have to wake up
to display that for your users.
So make sure you
use those carefully.
If you're constantly sending or
scheduling local notifications
or sending remote
notifications from your servers,
pretty much the device-the
user's device will be awake
for a majority of the time.
Now, one thing that you
guys might not know is
with the push notifications
you send,
you can actually
set a push priority.
You can actually set a bit
that will determine the
priority of your push.
Now, by default, your pushes
are sent with priority 10,
which is deliver this
immediately whenever you can,
so our servers will
attempt to do that.
However, if you're simply trying
to notify or alert the user
of something that is not needed
to be displayed immediately,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of something that is not needed
to be displayed immediately,
consider setting your
priority at five.
This will tell our servers to,
you know, send this push maybe
at a better time
for your device.
For instance, let's say another
immediate-another high priority
push is coming through,
then let's just go ahead
and send this push through
with the high priority push.
But it won't actively send
it unless it's a power
optimal time.
So, consider using this for
your push notifications.
Now, one thing we do want you
to start using notifications
for is VoIP.
Now, this is an area where
we've made great improvements
in iOS 8.
So, when VoIP was first
introduced in iOS,
we required the VoIP apps
to maintain their own
persistent connections.
This meant that periodic
keep-alives had to be sent
from your servers to the
VoIP app and vice-versa.
Now, if the device was asleep,
the device will need to wake
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Now, if the device was asleep,
the device will need to wake
up for your applications
to be able to respond
to these keep-alive packets
and this meant periodic wakes.
Not only was this
bad for battery life
but it also made developing
VoIP apps fairly complex,
as some of these algorithms
to manage our connections can
be very, very complicated.
To simplify the development
process as well
as conserve the battery
life on our systems,
in iOS 8 we've introduced a new
way to do VoIP and that's VoIP
through the PushKit framework.
This new VoIP mechanism
allows you to communicate
with your VoIP apps through
the Apple's Push Notification
Service, right?
In this way, you no longer need
to maintain your
persistent connections.
Meaning, you won't
have to constantly wake
up the device just so that
your connections won't timeout.
Now with these VoIP pushes,
your app will be allowed
to receive runtime to
process these VoIP pushes.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to receive runtime to
process these VoIP pushes.
So your app will get runtime
whenever a VoIP push is sent
to your device.
And the best of all is your apps
will be able to do this even
if the user terminated the app
or for any reason your
app was terminated.
[ Applause ]
Thank you.
Someone's enthusiastic
about this.
And this is great, right?
Previously, in iOS, if a user
terminates your VoIP app,
the connection is gone and you'd
no longer be able to communicate
with that device, your servers
won't be able to communicate
with the device anymore.
Now, we will do that
for you and so even
if the user terminates your app
or for any other reason
the app terminates,
you will still be
able to communicate
with your apps using
these VoIP pushes.
And finally, to help
with performance as well
as to improve the call
setup times, we'll allow you
to include up to a 4k
payload in this VoIP pushes.
This is a lot more than
the 256 bytes you get
with regular pushes.
So this should help improve
the performance as well
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So this should help improve
the performance as well
as the call setup times
of your applications.
Now, to adopt this new
framework, what you'll need
to do is link the new PushKit
framework, it's new in iOS 8,
and then you want
to register your app
to receive these
push notifications,
these VoIP pushes.
So, you'll do something like
this in your application,
didFinishLaunching delegate.
So, you want to create
a PKPushRegistry object
and then set the
delegate to yourself.
And we'll be implementing
two delegate methods
in a little bit.
But the main thing is you want
to set your desired
PushTypes to include VoIP.
And with these three
simple lines of code,
your app is now registered
to receive VoIP pushes.
I do want to remind
you guys with all-as
with all background
running modes, you will have
to make sure your app
has the VoIP background
remote requested.
So, now, let's talk about
the two delegate methods
that we're going to implement.
First, similar to Remote pushes,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
First, similar to Remote pushes,
you have to handle the push
token that's given to you.
Now, if you have both the Remote
push as well as the VoIP pushes
in your applications, you will
get two separate push tokens.
So, you'll need to send both
of those up to your servers
so you can communicate
with your app.
And then you have the delegate
to handle your incoming
push, right?
So this is where your app
will get-will start running
when a push comes.
So, here you can send
off your notifications
to your users-you know,
maybe it's the new interacting
notifications that you want
to display, right?-to alert the
users that a call has come in.
And here, you know, you
could probably begin to set
up your connections
with your servers
to begin establishing
your calls.
So, that's all you need
to do on the device side
to adopt this new
VoIP mechanism.
Now, on the server side, you'll
still need to be required
to do some more changes, right?
First, you want to go and
request the new push certificate
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
for VoIP and you can do this
through the Apple's
Developer Portal.
And then, simply send
your pushes with the token
that you got and this new
push-VoIP push certificate
that you have.
Now, one thing to note is
this mechanism will only work
with devices running iOS 8.
So if you try to send
these VoIP pushes
to devices running iOS
7, it will not work.
So you need-you'll want to make
sure that you maintain some type
of compatibility
on your servers.
But this is a new way that
we're introducing for you
to do VoIP communication
with your applications
and this is new in iOS 8.
All right, next, I want
to talk about Location.
Location is a great way
for you to include context
in the information you
provide for your users, right?
Knowing where users are or
users have been will allow you
to provide better suggestions,
more relevant resources as well
as location-specific information
that could be immediately useful
for your applications.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
for your applications.
For my wife and I, one
of the most commonly
used features we use
on our phone is the
location-based restaurant
suggestions, right?
That really helps us to
be able to procrastinate
in deciding what we want to eat.
So, Location is a
great tool to integrate
in with your applications.
However, getting a user's
location can be unnecessarily
expensive if you're not
using the right APIs.
So, I'm going to talk
a little bit just
to whet your guys' appetite
on a couple location APIs
that you can consider using,
and I'm going to first start
by talking about
continuous location updates.
So, the continuous location
updates is a great way for you
to get fine-grained user
data, fine-grained information
about where your
users have been.
Now, there's two things
I want to point out here.
First is that using continuous
location updates can prevent
your device from going to sleep.
Whenever you call
startUpdatingLocation,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Whenever you call
startUpdatingLocation,
the location hardware
will immediately begin
to stream location data
for your application.
Now, if you have the location
back on a running mode,
the device will have
to stay awake even
if your app is not
active to allow your app
to process this location data.
Now, if this location data is
coming in frequently enough,
usually because of the
accuracy that you sent,
your device will be kept
awake the whole time
and you'll be paying
the overhead cost
of keeping the device up while
you're receiving these updates.
Second, I talked about accuracy.
Accuracy makes a
difference, right?
Depending on the
accuracy level you set,
not only does this
affect the frequency
but core location uses
different technologies
to get a user's location to you.
Depending on the
accuracy level you set,
you will utilize
different technologies
and so it will have
varying amounts of cost
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to get the user's location.
And, in general, the more
accurate you request,
the more energy it's
going to be required
on the location hardware site
to get that information for you.
So, know that continuous
location updates,
especially a high accuracy, can
be really, really expensive,
so you'll only want to use
this if it's necessary, right?
And make sure you, you know,
turn off continuous location
updates whenever it's
not needed.
If you won't be needing
to process user's location
data immediately or live,
if you're not going to be
providing users any feedback
with the location data that you
get back, consider using some
of the other more power-friendly
APIs that will still alert you
of users' locations when
you are interested in them.
And one of those is
deferred location updates.
So, this is a great API to use
if your app requires GPS level
of accuracy but won't need to
process that data live, right?
What happens here is that the
location hardware will buffer
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
What happens here is that the
location hardware will buffer
that data before it gets
sent to your application.
Now, you'll still be paying the
cost of utilizing, you know,
in our case since you're
requesting GPS level
of accuracy, the GPS.
However, you will allow the
device to be able to go to sleep
for longer periods of time.
And this is a great
way to, you know,
save energy while
still being able to get
that fine-grained user
location that you want.
So, if your application,
let's say, wants some type
of historical information like,
let's say, a run track app
or maybe you're implementing
some type of life tracking app,
you want to consider using this
because this will still give you
that fine-grained
continuous location data
that you wanted but
at a lower cost.
Now, if you don't need
GPS level of accuracy,
consider using the Significant
Location Change API, right?
This API will signify
the location hardware
to only notify your app if
the user has moved, you know,
a certain amount of distance.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
a certain amount of distance.
And this is, you know, you
can expect pretty much updates
to come in, at least, when users
have traveled over 500 meters
or there's also a rate
limit of five minutes.
So, this is a great API to use
if your application is-needs
to update information
based on regions.
For instance, a weather
app, or let's say you want
to provide your users with
the nearest local attractions,
right?
And you want to use
significant location change
because now your device does not
have to wake up as frequently.
And finally, I wanted to
mention region monitoring.
This is a great API to use if
your application only cares
about when users are
entering or exiting
out of a specific
location, right?
You want to use this, let's say,
if you're developing an app,
let's say, of a museum, right?
You want to-when the user
gets closer to your museum,
you want to, let's
say, update the app
with the local attractions
that might be there.
You want to use a region
monitoring to set up a region
that you're interested in.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Once you do that, you can
happily allow the device
in your app to go sleep while
the location hardware will check
for you if you get closer
to that region and only wake
up your app when you do.
So, once again, all
these are great APIs
that will help you
reduce the amount
of time the device
needs to stay awake.
Oh, one more thing
about location.
Please, please remember to
stop your location updates
when they're not needed.
I can't tell you how many
times we've, you know,
received battery complaints
only to find that, you know,
some application
forgot to unsubscribe
to their location updates
when they're not needed.
Please double check,
triple check,
because location
updates can be expensive
and it can keep your
devices awake.
So, please don't forget to
stop your location updates
when they're not needed.
So, I'm going to go
through a quick example
of what this might look
like for your application.
So, let's say, today you're
developing a run tracking app
and your requirements are
when the users are running,
you want to keep
track of their route.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
you want to keep
track of their route.
And, you know, also you have
this local store that you want
to update your app whenever the
user gets close to that store
to send the latest coupons
or discounts, right?
You want to entice your users
to go into your local store.
So, one way to do this is to
use continuous location updates.
Now, obviously, right,
when you're doing
continuous location updates,
in your code it would probably
look something more like this.
Whenever I get a location
update, make sure I store
if the users are running.
If the users are not running,
then you can drop
that location data.
Also, I want to check, every
time I get location data,
if I'm close to my
local store, right?
That way I can update the
device with the new coupons
so that your users will
want to go to your store.
So, let's take a look
at what this will look
like for your device.
As you can see the device
is on even though, you know,
the users have no
idea that this is on
and you're utilizing the GPS,
so location hardware is on.
Now, as the user
starts running, right,
you're constantly updating the
route but your device has kept
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
you're constantly updating the
route but your device has kept
on for the whole time.
And then, let's say
the user stops running
and now they're walking,
walk-slowly walking.
You're checking and checking
and finally, you get close
to your local store and you
can update your coupons.
And so, as you can see,
both the GPS hardware
and the device is kept
on the whole time while
you're doing this.
Now, let's use some
of the location efficient
APIs to do this, right?
So, now, instead of using
continuous location updates,
let's set up the deferred
location updates as well
as the region monitoring.
So, first thing this will
do is it will, kind of,
clean up your code
a little bit, right?
In your region monitoring
delegate, all you need
to do is just-you know, in
that function whenever you get
that callback, just
simply pull your server
from the new coupons.
And then you have your
location callbacks,
and in that function all you
need to do is basically take
that chunk of data that the
location hardware has buffered
for you and, you
know, simply store it
if the users are running.
So, with that, let's take a look
at what this does
for your device.
So, as the user is running,
the device is asleep until oh,
I've run this long a distance.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
I've run this long a distance.
So, now, I can opt in my
route when the device is on.
And then, you know, the
device can happily go
to sleep while the user
continues to run and then oh,
significant distance
have reached,
let me once again get this data.
As you can see just from now,
you have already reduced
the amount of time
that the device needs to stay up
and thus reducing
the energy cost.
Now, the users are done
running so, you know,
we don't need continuous-we
don't need deferred location
updates anymore.
So, let's stop that.
And then as users are
walking, walking, walking, oh,
now you reach and monitor API
will kick in and alert your app
and then your app can
wake up to respond
and to get the latest
coupons, right?
These location efficient
APIs are a great way for you
to get the same location data
that you might be wanting,
but more energy efficiently.
And so, I talked about a couple.
There's also something
I did not talk about.
There's the iBeacons
here and AutoPause.
I didn't really have time
to talk too much about.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
But there's a great document
online that will allow you
to get best practices on
what to do, which APIs to use
when you're using location.
And also new in iOS 8, I wanted
to mention there is a
significant location
visited API.
So, if you didn't get to check
out the core location session
yesterday, you'll want to check
out what's new in core location.
All right, so last, I wanted
to spend just a little bit
of time talking about Bluetooth.
So, with the advancement
of Bluetooth low energy,
Bluetooth accessories are now
becoming increasingly popular.
And with Core Bluetooth
introduced in iOS 5,
it's now easier than ever to
integrate Bluetooth accessories
with your iOS app, right?
This will allow you to
provide rich features
such as health monitoring,
right, the new health kit.
This will allow you to
provide information for users
that will allow you to do
fitness and activity tracking
as well as, you know,
item proximity sensing.
So, we could probably
spend, like,
a whole 'nother hour
here just talking
about how to-what are the best
practices on how to communicate
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
about how to-what are the best
practices on how to communicate
with Bluetooth accessories,
you know, what's the best way,
what's the most energy
efficient way to design
and architect the communication.
But, due to the amount of time
that we have left, I simply want
to mention one thing, and
that's whenever you do Bluetooth
transfers, your device will
wake up for your app to be able
to receive it even if your app
is running in the background.
So, whenever I say
that, hopefully by now,
alarm bells begin to set
off in you heads, right?
"My device has to wake
up for my app to be able
to receive this data."
And, hopefully, this chart
somehow just, you know,
pops up in your head once
again, high overhead cost,
device wake, not
good for energy.
So, you want to think about
creative ways to minimize that.
So, once again, to help you
guys just kind of get some ideas
and visualize this, we're going
to go back to our running app
and let's say we integrated a
Bluetooth fitness band, right?
You now want to help track
user activity as well
as perhaps heart rate,
you know, blood pressure
when the users are running.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So, you know, we're going to use
an example that we've used over
and over again, and hopefully,
this idea will stick
in your head, right?
We're going to start by
just streaming the data.
All right.
So, now your fitness
data are coming in
and the device wakes up.
However, because you're
constantly streaming the device
can potentially be kept
up the whole time while
data is coming in.
So, our favorite solution
is buffering, right?
So, now, instead you're
buffering the location data
on your accessory site and
so your device doesn't have
to pay the cost to stay awake.
And once your buffer is full,
then you spend one
time-then you do, in one time,
to send all your data
over to your device
and the device only has to wake
up one time to process a lot
of data and then it
can go back to sleep.
Right, this is-this is something
we really want you to consider,
especially when you're not
really actively providing users
with information
immediately, right?
In our running tracking app,
this works because, you know,
our users probably won't be
checking their devices all the
time when they're running.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So, you can coalesce your data
transactions, doing more work
at once, so you can minimize
the number of times you need
to keep the device awake, right.
You don't want to implement a
cool fitness band that's going
to constantly keep the
users' device awake even
when they don't care
about the information.
Because then the users are
going to put off their device,
you're going to see that
little energy drain bar
and when the user
wakes up their device,
they're not going to be happy.
So, the best way really
to reduce the energy cost
of your application when it's
running in the background is
to do as much work as you
can when you do get to run
so that you can minimize
the number of times you have
to wake up, right, and
to coalesce your work
so you can cut down your wakes.
So, let's take this one
step further, right?
So, we've talked about this run
tracking app and we know that,
once again, we've implemented
a buffer on the Bluetooth side,
but we're also implementing
using the deferred location
updates, right?
So, these-both of those
have greatly minimized them
for wakes, but we
can even do better.
So, let's say, now instead
of having these two channels
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So, let's say, now instead
of having these two channels
of waking up your device,
let's coalesce them.
So, as users are running,
you're gathering data
on your fitness band.
But then, deferred location
updates wakes up your-wakes
up the device in your
app and says, "Hey,
you have location data."
While your device is
awake, let's just go ahead
and grab the data
from your fitness band
so you no longer have to
wake up the device again
when you have your
buffered data.
Then your app can process all
these things at once and go back
to sleep, helping
you conserve energy.
So. once again, the best way
for you to reduce energy use
of your app when it's
running in the background is
to coalesce your work so
you can cut down the wakes.
Cut down the number
of times you'll need
to wake up the device.
And especially in iOS, most
of the time you're running
in the background, you'll only
be doing a little bit of work
for short amounts of time.
So, you want to minimize
the number of times you do
that so you can help
produce energy.
So let's sum it up, right?
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So let's sum it up, right?
And I want to leave you
guys with this quote.
Hopefully, you guys are
all familiar with this:
"With great power comes
great responsibility."
We've provided many tools and
APIs for you to, you know,
build these amazing apps.
Use those responsibly, right?
Keep in mind energy as
you design your apps
because it greatly
impacts user experience.
Find creative ways in your
apps to do work less or never,
to do work more efficiently and
to do work at a better time.
And finally, like
we talked about,
be a considerate background app.
Your users have no idea
of what you're doing
when you're running
in the background.
So, be considerate.
Think of ways to coalesce
your work so you can cut
down the number of wakes.
So, for more information,
we have here Paul and Jake
as our evangelists
that you can contact.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And also, here are
some related sessions
that might be and-that you
might be interested in.
Now, some of these have passed
already so I would recommend you
to go download the
videos, right?
For the graphic and
animations, it really goes
into details about, you know,
how to-how the graphic
rendering pipeline works
and how blur works.
And then you have, obviously,
"What's New in Core Location"
as well as "What's New in
Foundation Networking."
And then you have
the instruments,
"Improving Your Apps
with Instruments."
Also, like I said, download
Anthony's talk earlier.
Thank you so much for coming.
Enjoy your lunch.
[ Applause ]