Transcript
[ Music ]
[ Applause ]
>> Hi. I'm Roberto from the
Software Battery Life Team here
at Apple, and I'm really excited
to talk to you guys today about
advances in App Background
Execution.
Users love apps, and they love
apps because of the many great
experiences that they enable.
Some of these are in the
foreground when the user is
actively using the app, and some
of these are in the background
and require background execution
in order to be possible.
So, if we come up with a list of
what might require background
execution, we might come up with
something like this, things like
navigation or accessory
communication or maybe periodic
updates and downloads.
And at Apple, we design APIs
that give background execution
to enable these use cases and
experiences.
So, today, we'll talk about
background execution, give a
general overview, then talk
about some best practices to
follow, and finally, my
colleague, Thomas, will come up
to introduce a new background
task framework that offers new
background execution
opportunities.
Let's begin with an overview of
background execution.
We can begin by answering the
question of what is background
execution?
What do we mean?
So, when we think of background,
it can mean many things.
Background threads or background
queues, but when we're talking
about background execution,
we're talking about the app
running or executing code while
it's not in the foreground.
So, looking at this diagram,
we're talking about that third
box where the app is running in
the background but it's not
necessarily visible to the user.
Now, why do we enter this state?
It really boils down to two
ways.
The first is a request by the
app.
This applies to more generic
background execution where the
app wants to perform some work
and it makes a request to the
system.
You can think of something like
downloads or periodic updates or
maybe finishing some foreground
work.
The second way is through
specific event triggers where
the app gets background time in
order to respond to something
that happened in the world.
For example, maybe the user
entered some foreign region or
maybe there was some new health
data that your app needed to be
made aware of.
And when running in the
background, we care deeply about
user experience, so there are
many important factors we
consider when we design APIs to
support these use cases.
And I want to highlight three of
these.
Power, performance, and privacy.
So, let's start with power.
Whenever your app is running in
the foreground or the
background, it's using power,
and over time, this drains
energy and can drain battery.
To visualize this, let's take a
timeline.
The left might be the start of
the day when the sun rises, and
the right might be the end of
the day, and maybe the user
decides to plug in their device
towards the end of the day, and
we have that green charging
region to represent that.
So, we can plot the times our
app runs in the foreground in
these lightly shaded rectangles,
and we can plot the time the app
runs in the background in these
darkly shaded rectangles.
If we run for longer, then we'll
drain more battery.
If we run for less time, we'll
drain less battery.
And so, when we design APIs, we
focus on the right amount of
time to run to support specific
use cases while maintaining
great battery life, and when
using these APIs, think about
how to be efficient and alert
the system when you're done
doing work by calling completion
handlers.
That way, if the system gave
your app time to run, you can
tell the system, hey, I'm done a
little early.
It can suspend your app, and
you'll stop consuming the user's
battery.
Next, we have performance.
We want our system to perform as
smoothly as possible.
This means that we want fast app
launches and responsive UI, and
this is especially important
when we're running in the
background.
And the reason is that while we
may be tempted to think that
there's only one app running
over the course of the day,
there actually are multiple apps
running at different points in
time in the foreground and the
background.
And when we overlay this, we can
see that our app may be running
in the background while another
app is running in the foreground
or multiple apps can be running
in the background when an app is
running in the foreground.
And so when we design APIs, we
think about setting smart CPU
and memory limits to minimize
the impact on other usage, and
when using these APIs, you
should be aware of what these
limits are so that you don't
affect what the user is actively
doing but more importantly so
that the system doesn't
terminate your app, and then
when you launch in the future,
you might be slower to launch.
The last important factor is
privacy.
Users are really sensitive and
really care about their personal
data.
And, while they may be aware of
all the times that the app is
running in the foreground and
expected to have access to
certain pieces of information,
they may not be as aware of all
the times the app is running in
the background.
And so, when we design APIs,
this means that we have
different APIs for different use
cases, each with their own set
to a specific set of data that
it needs to support that.
And when using these APIs, think
about how to be transparent to
the user and let them know which
pieces of data you're using.
So, those were the three
important considerations to
consider in background
execution.
Power, performance, and privacy.
And when we go back to our list
of use cases, these different
use cases translate into
different APIs, each with a
different set of requirements to
achieve the desired behavior
while maintaining a great user
experience.
So now that we have a general
overview, let's dive into some
best practices to follow for
some specific modes and talk
about changes to APIs.
So, to do so, let's take a
messaging app.
It might have core
functionality, like sending
messages or making phone calls,
and then it might have some
bonus features like allowing the
user to mute a chatty thread or
downloading past attachments.
And so now, I'll go through each
of these four and talk about
which API we would use to
accomplish this.
The first is sending messages.
Sending messages is a core
functionality of your app.
If I'm sending a message to my
friend, I want it to be
delivered right now, not a day
from now or a week from now.
And so the user expects that
this will complete immediately.
And while this may usually be
very quick, sometimes this might
not be the case.
Maybe the network's congested or
maybe the backend servers are
slow, and it might take
additional time to send the
message.
And in this additional time, the
user might even leave the app or
put their phone down and lock
it.
And so, we need a way to protect
the completion of this task.
We need to make sure that our
message finishes sending so that
the user doesn't come back to
the app and realize, hey, why
didn't my message send to my
friend.
The API to use for this is
background task completion.
This gives apps additional run
time to run in the background
before being suspended.
To do it, you call UI
Application Begin Background
Task or you call Process Info
Perform Expiring Activity if
you're running from an
extension.
And again, this is to complete
work that started in the
foreground.
So, other examples might include
saving files to disk or
completing any user-initiated
requests.
So, let's take a look at what
this might look like in code for
our sending messages example.
Here, we have our send message
function.
In it, after we establish our
send operation, we call Begin
Background Task.
This lets the system know that,
hey, we're starting a task that
even if the user backgrounds the
app, let us continue running to
finish it.
Then, when we finish sending the
message, in our completion
block, we'll let the system know
to end the background task.
We don't need time anymore.
This is for cases when the
system might have given us time,
the user backgrounds the app,
and we're done, and we can tell
the system to suspend us so that
we stop draining the user's
battery or potentially affect
their performance.
And there's one last thing I
want to highlight.
There are some cases where maybe
the system gives you additional
time, but conditions were so
bad, maybe it was so congested
that even with the additional
time we didn't finish.
And so, we have an expiration
handler for that, and the system
will call the expiration handler
at this point in time.
So, in this case, we post the
user notification to the user, a
local banner saying hey, hop
back into the app, because the
message didn't send.
So, to recap, when we're sending
messages, we want to protect
that it sends by using
background task completions, and
we want to make sure we start it
based on the user action.
We don't want to wait until we
enter the background to call
begin background task, because
if we do so, then we're limiting
the amount of time we're
actively trying to send it
before the system may suspend
our app.
So, now that we've talked about
sending messages, let's talk
about phone calls.
So, I may want to message my
friends all the time, but
sometimes you just want to get
on the phone and quickly hop in
a phone call and tell them
something so you don't have to
type it out.
There's an API for this, and
it's VoIP push notifications.
This is a special type of push
that launches your app and gives
it run time so that you can
present to the user the fact
that they're being called, and
the user can answer a phone
call.
To register for this, you simply
set the VoIP push type in your
PK push registry when you
register for your VoIP pushes.
And new this year, it's very
important that you know that you
must report incoming calls with
CallKit in the
didReceiveIncomingPush callback
or your app will be terminated.
And, if you repeatedly do this,
or if you repeatedly fail not to
report an incoming call, the
system may stop launching your
app for VoIP pushes altogether.
So, let's take a look at how we
might adapt to this change in
code.
Here, we have our
didReceiveIncomingPush callback,
and in it, we see that if the
push type is VoIP, we'll use the
information from our push
payload to populate a CX call
update object, and then we
report a new incoming call using
our CX provider.
And so, you need to make sure
that you report your incoming
calls before that method
returns, or your system, the
system will kill your app.
A couple other tips you might
want to take note of is that if
you include your caller info in
the push payload, then you'll
have all the information you
need to quickly present that
incoming call UI, and so try to
include as much information as
possible so that you can present
as rich of a UI as possible.
Second, make sure that you set
an apns-expiration on your push
to 0 or something small.
In this way, the user won't
receive a push minutes later for
a call that is no longer
dialing.
So, for example, if someone
called my device, and the push
took two minutes to send, then I
wouldn't want to report an
incoming call once the push
comes in two minutes later
because that person is probably
not dialing anymore.
So, if we set the expiration to
0, which means deliver
immediately or fail or something
on the order of a few seconds,
then we know that if the device
is receiving a push, it's still
for a phone call that's
relevant.
And, it's important to note that
if you prefer a banner, you can
always just use standard pushes
instead of the full-- and that
way you don't have to a full
screen in call UI.
And you can use a notification
service extension if you need to
modify the content of your
pushes.
For example, if you needed to
Decrypt them.
So that was VoIP pushes and
phone calls.
Now let's talk about muted
threads.
So, when a user has a messaging
app, they might be messaging a
lot of different friends or a
lot of different groups of
friends, and some of these
threads can get pretty chatty,
and the user might not want an
alert for that specific thread.
But, the content may still be
relevant.
It may still be nice to have.
When the user hops back into the
app, they want to be able to see
the message.
They just don't want to be told
every time the message is coming
in by having their device
vibrate.
And so we need a way to alert
the device but not the user
about content being available.
To do so, you should use
background pushes.
Background pushes are a
mechanism to tell the device
that new data is available
without alerting the user.
To use these, you just send a
push with the content available
flag set to 1 without an alert,
a sound, or a badge.
And then the system will
intelligently decide when to
launch the app to download the
content based on power and
performance impacts and trying
to minimize that.
So, to look at this on a
timeline, it might look
something like this.
The user might foreground the
app and decide to mute a
specific thread.
Then at some point in the
future, someone might message
that thread, and a background
push will be received by the
device.
Sometime after that, the app
will be launched and get some
run time to fetch that content,
and then when the user comes
back to the app later on in the
day and foregrounds it, the user
will open the thread and see
that the content is right there.
And, there's some very important
new things with background
pushes too.
You must set the apns-priority
to 5 or your app will not
launch, and you should also set
the apns push type to
background.
This is required for watchOS but
we highly recommend it for all
platforms.
And if you want more information
about pushes on watchOS, you can
see the creating independent
watch app session from
yesterday.
So to recap, for muted threads,
use background pushes as a best
effort way to download the
content, and in cases where
maybe the app didn't runtime
after the background push was
received, you can always just
download that content when the
app re-enters the foreground.
Now, let's talk about
downloading past attachments.
Say the user signs into their
account, and it's a new device.
There might be some content that
they immediately want from their
account, like their conversation
list or some recent messages.
But there might be a bunch of
older content that it would be
nice if you didn't have to
download it right when the user
was in the app.
To visualize this, why download
it while the user is in the
foreground and potentially
impact their performance or
battery life when we could do it
at some point when the device is
charging and idle.
The way to do this is to use a
discretionary background URL
session.
This allows the system to defer
a download until a better time,
and it's an API that allows you
to pass more information so that
it can be smarter about
scheduling it.
To use it, you simply set up a
background URL session the way
you would normally, and then you
set your discretionary flag to
true.
Let's take a look at some of
those additional information
that you might be able to pass
in so that the system can be
smarter.
You can pass in timeout
intervals.
Maybe you don't want it to keep
attempting to download
something, so you want to bound
how long it does it.
You could pass in an earliest
begin date.
Maybe you don't want to do an
upload or a download until some
point later in the future.
And, you can pass in an expected
workload size so that the system
knows how much work it should
expect to do when it runs your
download.
So, when you're downloading past
attachments, you want to defer
the work if possible to minimize
the user-visible impact, and we
can apply the same principles to
any deferrable download or
upload.
So, maybe you have some
analytics you want to batch
together and upload at a better
time, or maybe you have some
photos the user took that you
want to back up later on.
So, to recap, we had a messaging
app, and it features some core
functionality.
You can send messages, make
phone calls, allow the user to
mute threads, and you can
download past attachments.
And we used a different API to
accomplish each of these.
We used a background task
completion to guard that our
message would send even if the
user left the app.
We used VoIP pushes as a way to
enable phone calls.
We used background pushes as a
best effort way to give the app
runtime to respond to new
content, and we used a
discretionary URL session to
download attachments at the
right time.
But there are still many other
uses cases that are not
currently covered by our
existing modes, and so I'd like
to invite my colleague, Thomas,
up to introduce a new background
mode and framework designed
specifically for these use
cases.
Thanks everyone.
[ Applause ]
>> Thanks, Roberto.
So let's talk about these use
cases.
These are things like actively
syncing state with the server,
cleaning up your databases, or
backing up data to the Cloud.
These are deferrable,
maintenance-type tasks that
ideally you would want to do in
the background in order not to
impact foreground user activity.
And what we see today, is that
apps are doing this kind of work
right as they enter the
background, and over the course
of an entire day, that can
really add up.
So, what if you could take all
that work and defer it to later,
perhaps when the device is on
charger and otherwise idle?
That's what we're introducing
this year with a brand-new
background mode and framework to
go along with it that we call
background tasks.
Background tasks let's you
schedule work to do in the
background later.
We're making it available on
iOS, iPadOS, tvOS, and iPad apps
on the Mac.
And not only are we introducing
this new background mode we call
background processing tasks,
we're also taking this
opportunity to refine the
existing API for background app
refresh.
So, let's talk about the new
background mode.
Background processing tasks give
your apps several minutes of
runtime at system friendly times
so you can do that deferrable
maintenance-level work I
mentioned earlier as well as
entirely new things, such as
on-device, Core ML, training and
inference in the background,
which you can learn about in
their session.
CPU Monitor is a feature in our
systems that automatically
terminates apps that use too
many CPU cycles in the
background in order to protect
user's battery life.
For the first time ever, we're
giving you the ability to turn
that off for the duration of
your processing task so you can
take full advantage of the
hardware while the device is
plugged in.
And finally, we'll make sure
that you're eligible to run
these tasks as long as you
request them when your app is
foreground or if your app has
been recently used in the
foreground.
Next, I'd like to talk about the
new API for background app
refresh.
This is a new API, but it has
the same policies as the
existing one, which means your
app will get 30 seconds of run
time, each time it's launched,
to fetch new content and keep
itself up to date throughout the
day.
How often your app is launched
and at what times depends on how
the user has historically used
your app.
So, if the user typically uses
your app in the morning, the
afternoon, and the evening, the
system will learn this pattern
and start launching your app
shortly before those times so
you have an opportunity to get
the content you need.
This also means that an app that
isn't used as frequently may not
launch as frequently as well.
As I mentioned, this is the new
API for background app refresh,
so we're deprecating the
existing ones on UI Application
seen here.
These APIs will continue the
work the same way they do today
on iOS, iPadOS, and tvOS devices
but they're not supported on
Mac, so make sure to adopt
background tasks for background
app refresh on Mac.
Let's take a look at how this
API works.
Let's say we have an app and a
couple of extensions that's
contained within.
The primary object that you will
be interacting with is the BG
Task Scheduler.
The BG Task Scheduler is your
interface to the systems
intelligent, dynamic activity
scheduler that constantly
monitors various system
conditions including battery
level, app usage, network
connectivity, and more.
While your app is running, you
can request that it be woken up
later to do work in the
background.
To do so, you'll create an
instance of a BG task request
object that corresponds to the
type of task you would like to
perform.
In this case, we want to do a
background app refresh so I made
a BG app refresh task request.
And we submit that to the
scheduler.
If you have several types of
tasks you want to do, you can
submit multiple requests.
In this case, we also want to do
some database cleanup, so I'm
going to make a BG processing
task request and submit it as
well.
You can also submit requests
from an extension while it's
running.
So, if our keyboard extension
wants to do some learning based
on the user's typing habits, it
can create a BG processing task
request and submit it too.
As you can see, you can have
multiple pending BG processing
task requests, each representing
a specific task you would like
your app to do.
Now the scheduler knows about
all the tasks our app would like
to do, and when all the
necessary system conditions and
policies are satisfied, it's
going to fulfill that task by
waking up your app and launching
it into the background and
deliver it a BG task object
corresponding to that task you
should perform.
In this case, we got a BG app
refresh task, so we can now
perform a background app
refresh, fetch content, and
update our UI.
When we're done, we're going to
call set task completed, and
that's going to mark the task
complete and allow your app to
suspend.
Depending on how you configure
the BG task request and on
various system conditions and
policies, we may choose to
launch your app for multiple
tasks at the same time.
So, in this case, our app is
launched and delivered both BG
processing tasks requested
earlier.
When your app is launched, it's
given a finite amount of time to
complete the work it's given,
and that time is allotted per
launch, not per task, so you
should be prepared to
concurrently handle all the
tasks you're given in the time
allotted.
Also, note that that processing
task requested from the keyboard
extension was delivered to the
main app, and that's because
it's always the main containing
app that is launched to handle
background tasks, never
extensions.
When your app is done doing the
necessary work in call set task
completed on all of the BG task
objects it's given, it'll
suspend.
So that's a high-level overview
of how to use the background
task API.
You create and submit BG task
requests to the BG task
scheduler, wait for the system
to launch your app into the
background, perform the
necessary work, and then call
set task completed on the BG
task objects.
And to walk you through how you
would implement this in your
app, I'd like to step you
through a demo.
[ Applause ]
So this is our app.
It's called Color Feed, and it's
a typical social media type app
except instead of a feed of
photos, you get a feed of the
latest trending colors.
And what you can see is that I
can scroll through and see what
the latest colors are at various
points in time.
So, what I would really like to
do is make sure that my app can
keep itself up to date and
refresh itself throughout the
day without the user having to
go in and manually do that.
And the perfect tool for this is
background app refresh.
So, I'll implement it with
background tasks.
The first thing you need to do
is add the required keys to your
info.plist to declare your
support for background tasks and
background app refresh.
So, I'm going to go here into my
project settings, click on the
target for my app, and go into
the signing and capabilities
tab.
I'm going to click the plus sign
and add a new capability for
background modes.
As you can see, it added this
new section, and we need to
choose the right background mode
for this type of task.
In this case, for background app
refresh, the box is labeled
background fetch, just like with
the old API.
So, I'll go ahead and check
that.
Next, I'm going to head into my
ask info.plist file and I'm
going to click the plus sign to
add a new key.
This key is called permitted
background task scheduler
identifiers.
And it's an array of strings.
So, each string in this array
uniquely identifies a specific
task your app wants to do, and
it should be unique within your
app.
We recommend using reverse DNS
notation to avoid conflicts with
any third-party frameworks you
may be using.
So, here, I'm going to expand
that array and click the plus
sign to add a new string.
And I'll call it
com.colorfeed.refresh and hit
save.
Next, I need to actually
implement the code to handle
when my app is launched.
So, I'm going to do that in my
app delegate.
I'll head into my app delegate
file, and the first thing I need
to do is import background
tasks.
All right.
So, the next step is to tell the
system what you want to do when
you get launched, and you do
this by registering a launch
handler before your application
finishes launching.
So, in my application did finish
launching with options method,
I'm going to call BG task
scheduler shared, register for
task with identifier and then
pass in that same identifier I
just put into the info.plist.
The next parameter is a dispatch
queue, which is what my handler
will be called on, and I can
specify a queue that I'm using
if I want to synchronize with
other things in my app.
Or, I can pass in nil, and the
system will create a background
serial queue for me.
The next parameter is the actual
launch handler, which is what
will be called when my app is
launched for the task.
As you can see, it takes one
parameter of type BG task.
What I'm going to do is I'm
going to call a method I'll
write called handle app refresh
and downcast that BG task object
to a BG app refresh task since
this is used for background app
refreshes.
I'm going to go ahead and write
that handle app refresh method
now.
Here, I've written all the code
I need to actually perform the
background app refresh.
It's going to fetch the latest
content from the server, update
my database, and update my UI.
But to integrate this with
background tasks, there are two
things I need to do.
The first is to handle
expiration.
Your task is given a limited
amount of time to finish, so
when your time is almost out,
we'll warn you and give you a
chance to quickly wrap
everything up and complete your
work.
The system may also choose to
terminate your task early in
case the system decides the
conditions are no longer
sufficient for you to run your
task.
So what I'm going to do is set
an expiration handler on the
task, and in the expiration
handler, I'm going to call
cancel all operations on my
operation queue, and that's
going to stop all the work I'm
doing and cancel any work that I
haven't even started yet.
The next thing I need to do is
to call set task completed on
the task when I'm done with my
work.
I need to do this even if the
expiration handler has been
called.
And if I fail to do this, the
system may actually terminate my
app, and that will impact launch
performance later, and I
definitely don't want to do
that.
So, what I'm going to do is I'm
going to say that when my last
operation in that queue is
complete, I'm going to call set
task completed, and I'm taking
advantage of the fact that
operations always call their
completion block regardless if
they were cancelled early or
they finish normally, and I'm
using that to determine if my
task completed successfully or
not.
All right.
So the last step is to actually
schedule a BG task request, and
I'm going to do that when my app
enters the background because
that's when the user has stopped
using my app.
So, I'll go ahead and write that
now.
As you can see, I'm calling a
method I just wrote called
schedule app refresh, and my
application did enter background
method.
And here, I'm creating a BG app
refresh task request object and
passing in again that same
identifier.
I then submit to that request to
the being task scheduler.
There's one additional property
I want to call out on the being
task request object, and that's
earliest begin date.
This is how you can specify a
start delay for your task.
So, in this case, I'm saying
don't start app refresh, don't
start refreshing my app until at
least 15 minutes from when I
schedule it, and this lets me
get the same behavior as with
the old set minimum background
fetch interval API.
So, I schedule my task, but
there's actually one more thing
I'll probably want to do.
Because every single BG task
request object corresponds to
exactly one launch, right now if
my app gets launched for a
background app refresh, it won't
get launched again until the
user enters and then leaves my
app.
But I don't want that.
I want it to keep refreshing
throughout the day.
So, all I have to do is call
schedule app refresh in my
handle method.
So, I'll schedule another
request as soon as I get launch
for an existing one.
And that's it.
That's all the code I need to
write to handle background app
refresh in my app.
But, as I'm using my app and I'm
scrolling through, I'm seeing
that there are like a lot of
entries from a really long time
ago, things that are probably
not relevant to the user
anymore, and just taking up disk
space.
So it would be really great if
we could clean our database up
for the user, and the best tool
for that is BG processing tasks.
So, I'll go ahead and implement
that as well.
Just like before, I'm going to
head into my project settings,
the signing and capabilities
tab, and this time, in
background modes, I'm going to
make sure to check the
background processing checkbox.
I'll go into my info.plist and
hit the plus sign to add a new
identifier.
This one I'll call
com.colorfeed.dbcleaning and hit
save.
Then, just like before, I'll go
into my app delegate and call
register again.
Here, it's the same call I made
earlier, except I'm passing in
the new identifier, and I'm
calling handle database cleaning
and down casting to BG
processing task instead.
I'll go ahead and implement
handle database cleaning now.
I've already integrated this
code with background tasks, and
what it does is it just deletes
everything that's older than the
past day.
As you can see, I make sure to
set an expiration handler and
cancel all my operations, and I
make sure to call set task
completed with success when I'm
done.
One thing I am doing differently
here is I'm keeping track of the
last time I successfully cleaned
my database.
I'm doing this because when I
schedule my task, I want to be
conscientious of how I'm using
the system's resources.
I don't want to schedule a
database cleaning task every
time the user leaves my app if
my database doesn't actually
need to be cleaned.
So, what I'm going to do is
write a method called schedule
database cleaning if needed.
And here, I'm just checking that
if it hasn't been at least a
week since I last cleaned it,
don't do anything and bail out
immediately.
Otherwise, I'll go ahead and
schedule that request.
I'll create a BG processing task
request and pass it that same
identifier I did before.
There are some additional
properties on the BG processing
task request you may want to be
aware of.
The first is requires network
connectivity, and this actually
defaults to false.
You should make sure to set this
to true if you actually require
network connectivity for your
task because otherwise we may
launch your task at a time where
there is no network and you
won't be able to do much work.
In our case, we're doing some
local database maintenance, so
we can just leave this as false.
The next is requires external
power.
Now, depending on the specific
type of device and various
system conditions and policies,
we may tend to launch apps while
they're plugged in and on
charger anyway.
However, if you require, if you
are requiring yourself to do
intensive work and use a lot of
resources, we highly recommend
that you set this to true so
that you can preserve your
user's battery life.
Setting this to true is also how
you'll disable CPU monitor for
CPU intensive tasks.
And that's it.
I'm going to go ahead and submit
that request through the
scheduler, and I'm going to do
that, if I need to, when my
application enters the
background.
So that's all the code I need to
write to handle both background
app refresh and background
processing tasks.
But how do I know this all
works?
I'd like to think I'm perfect
program, but that's obviously
not always the case.
So, the best way to test this
end to end is to live on your
device and use it like a user
would, and that will make sure
that you can get the time and
policies that you need for your
specific task.
But, we also know that you don't
have time to just sit there and
wait when you're coding, so we
added a couple of methods you
can call in a debugger to debug
your use of background tasks,
and let me demonstrate that for
you now.
I'm going to build and run this
new version of background tasks
onto my iPhone.
As you see, it launched, and now
I'm going to send it into the
background to make sure that my
task requests are scheduled.
I'll bring it back, and here,
I'm going to click the pause
button right here on the bottom.
This will enter the debugger.
And I can see the console right
here.
What I'm going to do is I'm
going to paste in the command
that's available in our
documentation so no need to
write this down.
Here, all I have to do is
replace the word task identifier
with the identifier of the task
I want to simulate.
I want to test background app
refresh, so I'll type in
com.colorfeed.refresh.
But the steps are the same for
my processing task as well.
I'll hit enter, and as you can
see, the system has recognized
that I want to simulate a launch
for my background app refresh
task.
I'll hit the play button right
here.
And as you can see, the system
is starting my app refresh task,
and my app is actively
refreshing, and I can see that
it's working.
I'm going to hit pause again,
and this time I'll make sure
that expiration works as well,
because that's equally
important.
What I'm going to do is I'm
going to enter that same command
except this time I'm going to
replace the word launch with
expiration and hit enter.
As you can see, the system
recognized that I made this
request, and when I hit play,
you should see that my app has
successfully stopped refreshing,
recognized that it expired, and
mark the task complete.
So, I know now that my app has
both successfully implemented
and tested background app
refresh and background
processing tasks with background
tasks.
[ Applause ]
Thank you.
There are some additional
considerations that you may want
to be aware of when you use our
framework.
First, be aware of how you set
earliest begin date, and don't
set it too far in the future.
If you set it too far in the
future and the user doesn't come
back to your app in the
meantime, we may choose to not
launch your task at all, and
that's just to protect users
expectations and privacy.
A user that doesn't use your app
for months does not expect that
it suddenly starts running in
the background.
So, we recommend that you keep
earliest begin date, your delays
we earliest begin date, down to
a week or less.
Next, make sure that any files
you need to access during a
processing task are accessible
when the device is locked
because that's the time when
we'll typically launch your
task.
We do guarantee that we won't
start your task until the user
first unlocks their device, so
make sure any files you need to
access are at most file
protection type complete until
first user authentication.
We showed you how to implement
background app refresh in a
traditional single-window UIKit
app, but as you know, this year
we're introducing multiwindow
apps with a UIScene API.
If you're adopting that API, you
should make sure to call UI
application request scene
session refresh at the
appropriate time to tell the
system that the snapshot in the
app switcher needs to be
updated.
And you can find out details
about this in their
documentation.
And finally,
BGTaskScheduler.submit is a
blocking, synchronous call, and
we designed it that way because
that simplifies adoption when
you're scheduling, when you
enter the background, which is
the common case.
However, if you plan on
scheduling during more
performance-sensitive scenarios
such as when the app is being
launched, you'll want to make
sure to call it on a background
queue so you don't block the
main thread and impede your
launch performance.
All right.
So, to sum up what we talked
about today.
Be really thoughtful about how
you use your time in the
background and keep in mind, the
same things we do when we design
our APIs, things like power,
performance, and privacy.
Make sure to use the right
background mode to accomplish
the exact thing you want to do
and enable the exact user
experience you want.
And finally, use a new
background task API to schedule
work to do in the background
later.
Use BG app refresh task to keep
your app content up to date
throughout the day and BG
processing task to do deferrable
maintenance-level work when the
device is idle and on charger.
For more information, stop by
our labs or visit our session
website, for links to
documentation and sample code.
Thank you.
[ Applause ]