Background Modes Tutorial: Getting Started
In this tutorial, you’ll create an app that uses audio playback, location updates, critical tasks, and background fetch to learn about the most common background modes. By Chuck Krutsinger .
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Background Modes Tutorial: Getting Started
25 mins
- Getting Started
- Playing Audio
- Giving Credit Where Credit Is Due
- Testing Audio in the Background
- Receiving Location Updates
- Enabling Location Updates
- Testing Location Mode in the Background
- Completing Critical Tasks Upon Moving to the Background
- When to Use Task Completion
- Setting Up a Completion Task
- Ending the Completion Task
- Registering and Ending Background Tasks
- Background Fetch
- Understanding Background Fetch
- Implementing Background Fetch
- Testing Background Fetch
- Where to Go From Here?
Testing Location Mode in the Background
If you exit the app, you should see the app update the location in your console log. Open it again to see all the pins on the map showing the places you went during your walk.
If you’re using the simulator, you can use it to simulate movement, too! Check out the Features ▸ Location menu:
Easy-peasy, right? On to the third tab and the third background mode!
Completing Critical Tasks Upon Moving to the Background
The next background mode is officially called Extending Your App’s Background Execution Time. What a mouthful. Task Completion is a bit easier to say!
Technically, this is not a background mode at all. You don’t have to declare that your app uses it in Capabilities. It’s an API that lets you run arbitrary code for a finite period when your app is in the background, giving you more time to finish critical tasks like saving data.
When to Use Task Completion
A valid use case of the Completion background mode is to complete some critical task, such as saving the user’s input or posting a transaction. There are many possibilities.
As the code is arbitrary, you can use this API to do pretty much anything: perform lengthy calculations, apply filters to images, render a complicated 3D mesh — whatever! Your imagination is the limit, as long as you keep in mind that you only get some time, not unlimited time. In a few moments, you’ll set up a lengthy calculation to run in the background, so you can see how this API works.
iOS determines how much time you get after your app moves to the background. There are no guarantees on the time you’re granted, but you can always check UIApplication.shared.backgroundTimeRemaining
. This will tell you how much time you have left.
The general, observation-based consensus is that you get about 30 seconds. Again, there are no guarantees, and the API documentation doesn’t even give an estimate — so don’t rely on this number. You might get five minutes or five seconds, so your app needs to be prepared for an interruption. iOS will signal you with a callback when your time is about up.
Setting Up a Completion Task
Here’s a common task that every computer science student should be familiar with: Calculating numbers in the Fibonacci Sequence. The twist here is that you’ll calculate these numbers after the app moves to the background!
Open CompleteTaskModel.swift and take a look at what’s there already. As it stands, this view will calculate Fibonacci numbers sequentially and display the result.
If you were to suspend the app on an actual device right now, the calculations would stop and pick up where they were once the app became active again. Your task is to create a background task so the calculation can keep running until iOS says, “Time’s up!”
You first need to add the following to CompleteTaskModel
:
var backgroundTask: UIBackgroundTaskIdentifier = .invalid
This property identifies the task request to run in the background.
Next add the following method to CompleteTaskModel
just before resetCalcuation()
:
func registerBackgroundTask() {
backgroundTask = UIApplication.shared.beginBackgroundTask { [weak self] in
print("iOS has signaled time has expired")
self?.endBackgroundTaskIfActive()
}
}
registerBackgroundTask()
tells iOS that you need more time to complete whatever it is that you’re doing when the app moves to the background. The value returned is an identifier for this task so that you can tell iOS when you are done. After this call, if your app moves to the background, it will still get CPU time until you call endBackgroundTask(_:)
.
Well, some amount of CPU time, at least.
Ending the Completion Task
If you don’t call endBackgroundTask(_:)
after a period of time in the background, iOS will call the closure defined when you called beginBackgroundTask(expirationHandler:)
. This gives you a chance to stop executing code.
So it’s a good idea to call endBackgroundTask(_:)
to tell the system that you’re done. If you don’t call it and continue to execute code after this block runs, iOS will terminate your app!
Add this method just below registerBackgroundTask()
:
func endBackgroundTaskIfActive() {
let isBackgroundTaskActive = backgroundTask != .invalid
if isBackgroundTaskActive {
print("Background task ended.")
UIApplication.shared.endBackgroundTask(backgroundTask)
backgroundTask = .invalid
}
}
This will end the background task if it is actively registered and reset its ID to invalid.
Registering and Ending Background Tasks
Now, for the important part: updating onChangeOfScenePhase(_:)
to register and end the background task depending on whether the app is moving to the background or active state.
Replace the two case statements with the following:
case .background:
let isTimerRunning = updateTimer != nil
let isTaskUnregistered = backgroundTask == .invalid
if isTimerRunning && isTaskUnregistered {
registerBackgroundTask()
}
case .active:
endBackgroundTaskIfActive()
When changing to background state, this registers the task when it’s running but not registered. When changing to active state, it’ll end the background task.
In beginPauseTask()
, add this line right after updateTimer = nil
:
endBackgroundTaskIfActive()
Now, when the user stops calculations, you call endBackgroundTask(_:)
to indicate to iOS that you don’t need any extra CPU time.
endBackgroundTask(_:)
every time you call beginBackgroundTask(expirationHandler:)
. If you call beginBackgroundTask(expirationHandler:)
twice and only call endBackgroundTask(_:)
for one of the tasks, you’re still going to get CPU time until you call endBackgroundTask(_:)
a second time with the second background task’s identifier.Build and run, then switch to the third tab.
Tap Play and watch the app calculate those sweet Fibonacci values. Send the app to the background, but watch the output in Xcode’s console. Your app should continue updating the numbers while the time remaining goes down.
In most cases, this time will start with 30 and go down to five seconds. If you wait for the time to expire when you reach five seconds — or whatever value you see — iOS invokes the expiration block.
Your app should stop generating output soon afterward. Then, if you go back to the app, the timer should start firing again, and the Fibonacci madness will continue.
Switch between active and background to see how you get an additional block of time with each switch.
On to the final topic for this background modes tutorial: background fetch.