App States and Multitasking

For iOS apps, it is crucial to know whether your app is running in the foreground or the background. Because system resources are more limited on iOS devices, an app must behave differently in the background than in the foreground. The operating system also limits what your app can do in the background in order to improve battery life and to improve the user’s experience with the foreground app. The operating system notifies your app whenever it moves between the foreground and background. These notifications are your chance to modify your app’s behavior.

While your app is active and in the foreground, the system sends touch events to it for processing. The UIKit infrastructure does most of the hard work of delivering events to your custom objects. All you have to do is override methods in the appropriate objects to process those events. For controls, UIKit simplifies things even further by handling the touch events for you and calling your custom code only when something interesting happens, such as when the value of a text field changes.

As you implement your app, follow these guidelines:

Managing App State Changes

At any given moment, your app is in one of the states listed in Table 3-1. The system moves your app from state to state in response to actions happening throughout the system. For example, when the user presses the Home button, a phone call comes in, or any of several other interruptions occurs, the currently running apps change state in response. Figure 3-1 shows the paths that an app takes when moving from state to state.

Table 3-1  App states

State

Description

Not running

The app has not been launched or was running but was terminated by the system.

Inactive

The app is running in the foreground but is currently not receiving events. (It may be executing other code though.) An app usually stays in this state only briefly as it transitions to a different state.

Active

The app is running in the foreground and is receiving events. This is the normal mode for foreground apps.

Background

The app is in the background and executing code. Most apps enter this state briefly on their way to being suspended. However, an app that requests extra execution time may remain in this state for a period of time. In addition, an app being launched directly into the background enters this state instead of the inactive state. For information about how to execute code while in the background, see “Background Execution and Multitasking.”

Suspended

The app is in the background but is not executing code. The system moves apps to this state automatically and does not notify them before doing so. While suspended, an app remains in memory but does not execute any code.

When a low-memory condition occurs, the system may purge suspended apps without notice to make more space for the foreground app.

Figure 3-1  State changes in an iOS app

Most state transitions are accompanied by a corresponding call to the methods of your app delegate object. These methods are your chance to respond to state changes in an appropriate way. These methods are listed below, along with a summary of how you might use them.

The App Launch Cycle

When your app is launched, it moves from the not running state to the active or background state, transitioning briefly through the inactive state. As part of the launch cycle, the system creates a process and main thread for your app and calls your app’s main function on that main thread. The default main function that comes with your Xcode project promptly hands control over to the UIKit framework, which does most of the work in initializing your app and preparing it to run.

Figure 3-2 shows the sequence of events that occurs when an app is launched into the foreground, including the app delegate methods that are called.

Figure 3-2  Launching an app into the foreground
Application life cycle

If your app is launched into the background instead—usually to handle some type of background event—the launch cycle changes slightly to the one shown in Figure 3-3. The main difference is that instead of your app being made active, it enters the background state to handle the event and then is suspended shortly afterward. When launching into the background, the system still loads your app’s user interface files but it does not display the app’s window.

Figure 3-3  Launching an app into the background

To determine whether your app is launching into the foreground or background, check the applicationState property of the shared UIApplication object in your application:willFinishLaunchingWithOptions: or application:didFinishLaunchingWithOptions: delegate method. When the app is launched into the foreground, this property contains the value UIApplicationStateInactive. When the app is launched into the background, the property contains the value UIApplicationStateBackground instead. You can use this difference to adjust the launch-time behavior of your delegate methods accordingly.

About the main Function

Like any C-based app, the main entry point for an iOS app at launch time is the main function. In an iOS app, the main function is used only minimally. Its main job is to hand control to the UIKit framework. Therefore, any new project you create in Xcode comes with a default main function like the one shown in Listing 3-1. With few exceptions, you should never change the implementation of this function.

Listing 3-1  The main function of an iOS app

#import <UIKit/UIKit.h>
 
int main(int argc, char *argv[])
{
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([MyAppDelegate class]));
    }
}

The UIApplicationMain function takes four parameters and uses them to initialize the app. You should never have to change the default values passed into this function. Still, it is valuable to understand their purpose and how they start the app.

  • The argc and argv parameters contain any launch-time arguments passed to the app from the system. These arguments are parsed by the UIKit infrastructure and can otherwise be ignored.

  • The third parameter identifies the name of the principal app class. This is the class responsible for running the app. It is recommend that you specify nil for this parameter, which causes UIKit to use the UIApplication class.

  • The fourth parameter identifies the class of your custom app delegate. Your app delegate is responsible for managing the high-level interactions between the system and your code. The Xcode template projects set this parameter to an appropriate value automatically.

Another thing the UIApplicationMain function does is load the app’s main user interface file. The main interface file contains the initial view-related objects you plan to display in your app’s user interface. For apps that use use storyboards, this function loads the initial view controller from your storyboard and installs it in the window provided by your app delegate. For apps that use nib files, the function loads the nib file contents into memory but does not install them in your app’s window; you must install them in the application:willFinishLaunchingWithOptions: method of your app delegate.

An app can have either a main storyboard file or a main nib file but it cannot have both. Storyboards are the preferred way to specify your app’s user interface but are not supported on all versions of iOS. The name of your app’s main storyboard file goes in the UIMainStoryboardFile key of your app’s Info.plist file. (For nib-based apps, the name of your main nib file goes in the NSMainNibFile key instead.) Normally, Xcode sets the value of the appropriate key when you create your project, but you can change it later if needed.

For more information about the Info.plist file and how you use it to configure your app, see “The Information Property List File.”

What to Do at Launch Time

When your app is launched (either into the foreground or background), use your app delegate’s application:willFinishLaunchingWithOptions: and application:didFinishLaunchingWithOptions: methods to do the following:

  • Check the contents of the launch options dictionary for information about why the app was launched, and respond appropriately.

  • Initialize the app’s critical data structures.

  • Prepare your app’s window and views for display.

    Apps that use OpenGL ES should not use this method to prepare their drawing environment. Instead, they should defer any OpenGL ES drawing calls to the applicationDidBecomeActive: method.

If your app does not automatically load a main storyboard or nib file at launch time, you can use the application:willFinishLaunchingWithOptions: method to prepare your app’s window for display. For apps that support both portrait and landscape orientations, always set up the root view controller of your main window in a portrait orientation. If the device is in a different orientation at launch time, the system tells the root view controller to rotate your views to the correct orientation before displaying the window.

Your application:willFinishLaunchingWithOptions: and application:didFinishLaunchingWithOptions: methods should always be as lightweight as possible to reduce your app’s launch time. Apps are expected to launch and initialize themselves and start handling events in less than 5 seconds. If an app does not finish its launch cycle in a timely manner, the system kills it for being unresponsive. Thus, any tasks that might slow down your launch (such as accessing the network) should be executed asynchronously on a secondary thread.

When launching into the foreground, the system also calls the applicationDidBecomeActive: method to finish the transition to the foreground. Because this method is called both at launch time and when transitioning from the background, use it to perform any tasks that are common to the two transitions.

When launching into the background, there should not be much for your app to do except get ready to handle whatever event arrived.

Responding to Interruptions

When an alert-based interruption occurs, such as an incoming phone call, the app moves temporarily to the inactive state so that the system can prompt the user about how to proceed. The app remains in this state until the user dismisses the alert. At this point, the app either returns to the active state or moves to the background state. Figure 3-4 shows the flow of events through your app when an alert-based interruption occurs.

Figure 3-4  Handling alert-based interruptions

In iOS 5, notifications that display a banner do not deactivate your app in the way that alert-based notifications do. Instead, the banner is laid along the top edge of your app window and your app continues receive touch events as before. However, if the user pulls down the banner to reveal the notification center, your app moves to the inactive state just as if an alert-based interruption had occurred. Your app remains in the inactive state until the user dismisses the notification center or launches another app. At this point, your app moves to the appropriate active or background state. The user can use the Settings app to configure which notifications display a banner and which display an alert.

Pressing the Sleep/Wake button is another type of interruption that causes your app to be deactivated temporarily. When the user presses this button, the system disables touch events, moves the app to the background but sets the value of the app’s applicationState property to UIApplicationStateInactive (as opposed to UIApplicationStateBackground), and finally locks the screen. A locked screen has additional consequences for apps that use data protection to encrypt files. Those consequences are described in “What to Do When an Interruption Occurs.”

What to Do When an Interruption Occurs

Alert-based interruptions result in a temporary loss of control by your app. Your app continues to run in the foreground, but it does not receive touch events from the system. (It does continue to receive notifications and other types of events, such as accelerometer events, though.) In response to this change, your app should do the following in its applicationWillResignActive: method:

  • Stop timers and other periodic tasks.

  • Stop any running metadata queries.

  • Do not initiate any new tasks.

  • Pause movie playback (except when playing back over AirPlay).

  • Enter into a pause state if your app is a game.

  • Throttle back OpenGL ES frame rates.

  • Suspend any dispatch queues or operation queues executing non-critical code. (You can continue processing network requests and other time-sensitive background tasks while inactive.)

When your app is moved back to the active state, its applicationDidBecomeActive: method should reverse any of the steps taken in the applicationWillResignActive: method. Thus, upon reactivation, your app should restart timers, resume dispatch queues, and throttle up OpenGL ES frame rates again. However, games should not resume automatically; they should remain paused until the user chooses to resume them.

When the user presses the Sleep/Wake button, apps with files protected by the NSFileProtectionComplete protection option must close any references to those files. For devices configured with an appropriate password, pressing the Sleep/Wake button locks the screen and forces the system to throw away the decryption keys for files with complete protection enabled. While the screen is locked, any attempts to access the corresponding files will fail. So if you have such files, you should close any references to them in your applicationWillResignActive: method and open new references in your applicationDidBecomeActive: method.

Adjusting Your User Interface During a Phone Call

When the user takes a call and then returns to your app while on the call, the height of the status bar grows to reflect the fact that the user is on a call. Similarly, when the user ends the call, the status bar height shrinks back to its regular size.

The best way to handle status bar height changes is to use view controllers to manage your views. When installed in your interface, view controllers automatically adjust the height of their managed views when the status bar frame size changes.

If your app does not use view controllers for some reason, you must respond to status bar frame changes manually by registering for the UIApplicationDidChangeStatusBarFrameNotification notification. Your handler for this notification should get the status bar height and use it to adjust the height of your app’s views appropriately.

Moving to the Background

When the user presses the Home button, presses the Sleep/Wake button, or the system launches another app, the foreground app transitions to the inactive state and then to the background state. These transitions result in calls to the app delegate’s applicationWillResignActive: and applicationDidEnterBackground: methods, as shown in Figure 3-5. After returning from the applicationDidEnterBackground: method, most apps move to the suspended state shortly afterward. Apps that request specific background tasks (such as playing music) or that request a little extra execution time from the system may continue to run for a while longer.

Figure 3-5  Moving from the foreground to the background

What to Do When Moving to the Background

Apps can use their applicationDidEnterBackground: method to prepare for moving to the background state. When moving to the background, all apps should do the following:

  • Prepare to have their picture taken. When the applicationDidEnterBackground: method returns, the system takes a picture of your app’s user interface and uses the resulting image for transition animations. If any views in your interface contain sensitive information, you should hide or modify those views before the applicationDidEnterBackground: method returns.

  • Save user data and app state information. All unsaved changes should be written to disk when entering the background. This step is necessary because your app might be quietly killed while in the background for any number of reasons. You can perform this operation from a background thread as needed.

  • Free up as much memory as possible. For more information about what to do and why this is important, see “Memory Usage for Background Apps.”

Your app delegate’s applicationDidEnterBackground: method has approximately 5 seconds to finish any tasks and return. In practice, this method should return as quickly as possible. If the method does not return before time runs out, your app is killed and purged from memory. If you still need more time to perform tasks, call the beginBackgroundTaskWithExpirationHandler: method to request background execution time and then start any long-running tasks in a secondary thread. Regardless of whether you start any background tasks, the applicationDidEnterBackground: method must still exit within 5 seconds.

Depending on the features of your app, there are other things your app should do when moving to the background. For example, any active Bonjour services should be suspended and the app should stop calling OpenGL ES functions. For a list of things your app should do when moving to the background, see “Being a Responsible Background App.”

Memory Usage for Background Apps

Every app should free up as much memory as is practical upon entering the background. The system tries to keep as many apps in memory at the same time as it can, but when memory runs low it terminates suspended apps to reclaim that memory. Apps that consume large amounts of memory while in the background are the first apps to be terminated.

Practically speaking, your app should remove strong references to objects as soon as they are no longer needed. Removing strong references gives the compiler the ability to release the objects right away so that the corresponding memory can be reclaimed. However, if you want to cache some objects to improve performance, you can wait until the app transitions to the background before removing references to them.

Some examples of objects that you should remove strong references to as soon as possible include:

  • Image objects

  • Large media or data files that you can load again from disk

  • Any other objects that your app does not need and can recreate easily later

To help reduce your app’s memory footprint, the system automatically purges some data allocated on behalf of your app when your app moves to the background.

  • The system purges the backing store for all Core Animation layers. This effort does not remove your app’s layer objects from memory, nor does it change the current layer properties. It simply prevents the contents of those layers from appearing onscreen, which given that the app is in the background should not happen anyway.

  • It removes any system references to cached images. (If your app does not have a strong reference to the images, they are subsequently removed from memory.)

  • It removes strong references to some other system-managed data caches.

Returning to the Foreground

Returning to the foreground is your app’s chance to restart the tasks that it stopped when it moved to the background. The steps that occur when moving to the foreground are shown in Figure 3-6. The applicationWillEnterForeground: method should undo anything that was done in your applicationDidEnterBackground: method, and the applicationDidBecomeActive: method should continue to perform the same activation tasks that it would at launch time.

Figure 3-6  Transitioning from the background to the foreground

Processing Queued Notifications at Wakeup Time

An app in the suspended state must be ready to handle any queued notifications when it returns to a foreground or background execution state. A suspended app does not execute any code and therefore cannot process notifications related to orientation changes, time changes, preferences changes, and many others that would affect the app’s appearance or state. To make sure these changes are not lost, the system queues many relevant notifications and delivers them to the app as soon as it starts executing code again (either in the foreground or background). To prevent your app from becoming overloaded with notifications when it resumes, the system coalesces events and delivers a single notification (of each relevant type) that reflects the net change since your app was suspended.

Table 3-2 lists the notifications that can be coalesced and delivered to your app. Most of these notifications are delivered directly to the registered observers. Some, like those related to device orientation changes, are typically intercepted by a system framework and delivered to your app in another way.

Table 3-2  Notifications delivered to waking apps

Event

Notifications

An accessory is connected or disconnected.

EAAccessoryDidConnectNotification

EAAccessoryDidDisconnectNotification

The device orientation changes.

UIDeviceOrientationDidChangeNotification

In addition to this notification, view controllers update their interface orientations automatically.

There is a significant time change.

UIApplicationSignificantTimeChangeNotification

The battery level or battery state changes.

UIDeviceBatteryLevelDidChangeNotification

UIDeviceBatteryStateDidChangeNotification

The proximity state changes.

UIDeviceProximityStateDidChangeNotification

The status of protected files changes.

UIApplicationProtectedDataWillBecomeUnavailable

UIApplicationProtectedDataDidBecomeAvailable

An external display is connected or disconnected.

UIScreenDidConnectNotification

UIScreenDidDisconnectNotification

The screen mode of a display changes.

UIScreenModeDidChangeNotification

Preferences that your app exposes through the Settings app changed.

NSUserDefaultsDidChangeNotification

The current language or locale settings changed.

NSCurrentLocaleDidChangeNotification

The status of the user’s iCloud account changed.

NSUbiquityIdentityDidChangeNotification

Queued notifications are delivered on your app’s main run loop and are typically delivered before any touch events or other user input. Most apps should be able to handle these events quickly enough that they would not cause any noticeable lag when resumed. However, if your app appears sluggish when it returns from the background state, use Instruments to determine whether your notification handler code is causing the delay.

An app returning to the foreground also receives view-update notifications for any views that were marked dirty since the last update. An app running in the background can still call the setNeedsDisplay or setNeedsDisplayInRect: methods to request an update for its views. However, because the views are not visible, the system coalesces the requests and updates the views only after the app returns to the foreground.

Handling iCloud Changes

If the status of iCloud changes for any reason, the system delivers a NSUbiquityIdentityDidChangeNotification notification to your app. The state of iCloud changes when the user logs into or out of an iCloud account or enables or disables the syncing of documents and data. This notification is your app’s cue to update caches and any iCloud-related user interface elements to accommodate the change. For example, when the user logs out of iCloud, you should remove references to all iCloud–based files or data.

If your app has already prompted the user about whether to store files in iCloud, do not prompt again when the status of iCloud changes. After prompting the user the first time, store the user’s choice in your app’s local preferences. You might then want to expose that preference using a Settings bundle or as an option in your app. But do not repeat the prompt again unless that preference is not currently in the user defaults database.

Handling Locale Changes Gracefully

If a user changes the current locale while your app is suspended, you can use the NSCurrentLocaleDidChangeNotification notification to force updates to any views containing locale-sensitive information, such as dates, times, and numbers when your app returns to the foreground. Of course, the best way to avoid locale-related issues is to write your code in ways that make it easy to update views. For example:

  • Use the autoupdatingCurrentLocale class method when retrieving NSLocale objects. This method returns a locale object that updates itself automatically in response to changes, so you never need to recreate it. However, when the locale changes, you still need to refresh views that contain content derived from the current locale.

  • Re-create any cached date and number formatter objects whenever the current locale information changes.

For more information about internationalizing your code to handle locale changes, see Internationalization Programming Topics.

Responding to Changes in Your App’s Settings

If your app has settings that are managed by the Settings app, it should observe the NSUserDefaultsDidChangeNotification notification. Because the user can modify settings while your app is suspended or in the background, you can use this notification to respond to any important changes in those settings. In some cases, responding to this notification can help close a potential security hole. For example, an email program should respond to changes in the user’s account information. Failure to monitor these changes could cause privacy or security issues. Specifically, the current user might be able to send email using the old account information, even if the account no longer belongs to that person.

Upon receiving the NSUserDefaultsDidChangeNotification notification, your app should reload any relevant settings and, if necessary, reset its user interface appropriately. In cases where passwords or other security-related information has changed, you should also hide any previously displayed information and force the user to enter the new password.

App Termination

Although apps are generally moved to the background and suspended, if any of the following conditions are true, your app is terminated and purged from memory instead:

If your app is running (either in the foreground or background) at termination time, the system calls your app delegate’s applicationWillTerminate: method so that you can perform any required cleanup. You can use this method to save user data or app state information that you would use to restore your app to its current state on a subsequent launch. Your method has approximately 5 seconds to perform any tasks and return. If it does not return in time, the app is killed and removed from memory.

Even if you develop your app using iOS SDK 4 and later, you must still be prepared for your app to be killed without any notification. The user can kill apps explicitly using the multitasking UI. In addition, if memory becomes constrained, the system might remove apps from memory to make more room. Suspended apps are not notified of termination but if your app is currently running in the background state (and not suspended), the system calls the applicationWillTerminate: method of your app delegate. Your app cannot request additional background execution time from this method.

The Main Run Loop

The main run loop of your app is responsible for processing all user-related events. The UIApplication object sets up the main run loop at launch time and uses it to process events and handle updates to view-based interfaces. As the name suggests, the main run loop executes on the app’s main thread. This behavior ensures that user-related events are processed serially in the order in which they were received.

Figure 3-7 shows the architecture of the main run loop and how user events result in actions taken by your app. As the user interacts with a device, events related to those interactions are generated by the system and delivered to the app via a special port set up by UIKit. Events are queued internally by the app and dispatched one-by-one to the main run loop for execution. The UIApplication object is the first object to receive the event and make the decision about what needs to be done. A touch event is usually dispatched to the main window object, which in turn dispatches it to the view in which the touch occurred. Other events might take slightly different paths through various app objects.

Figure 3-7  Processing events in the main run loop
Processing events in the main run loop

Many types of events can be delivered in an iOS app. The most common ones are listed in Table 3-3. Many of these event types are delivered using the main run loop of your app, but some are not. For example, accelerometer events are delivered directly to the accelerometer delegate object that you specify. For information about how to handle most types of events—including touch, remote control, motion, accelerometer, and gyroscopic events—see Event Handling Guide for iOS.

Table 3-3  Common types of events for iOS apps

Event type

Delivered to…

Notes

Touch

The view object in which the event occurred

Views are responder objects. Any touch events not handled by the view are forwarded down the responder chain for processing.

Remote control

First responder object

Remote control events are for controlling media playback and are generated by headphones and other accessories.

Motion

First responder object

Motion events reflect specific motion-related events (such as shaking a device) and are handled separately from other accelerometer-based events. .

Accelerometer

Core Motion

The object you designate

Events related to the accelerometer and gyroscope hardware are delivered to the object you designate.

Redraw

The view that needs the update

Redraw events do not involve an event object but are simply calls to the view to draw itself. The drawing architecture for iOS is described in Drawing and Printing Guide for iOS.

Location

The object you designate

You register to receive location events using the Core Location framework. For more information about using Core Location, see Location and Maps Programming Guide.

Some events, such as touch and remote control events, are handled by your app’s responder objects. Responder objects are everywhere in your app. (The UIApplication object, your view objects, and your view controller objects are all examples of responder objects.) Most events target a specific responder object but can be passed to other responder objects (via the responder chain) if needed to handle an event. For example, a view that does not handle an event can pass the event to its superview or to a view controller.

Touch events occurring in controls (such as buttons) are handled differently than touch events occurring in many other types of views. There are typically only a limited number of interactions possible with a control, and so those interactions are repackaged into action messages and delivered to an appropriate target object. This target-action design pattern makes it easy to use controls to trigger the execution of custom code in your app.

Background Execution and Multitasking

In iOS 4 and later, multitasking allows apps to continue running in the background even after the user switches to another app while still preserving battery life as much as possible. Most apps are moved to the suspended state shortly after entering the background. Only apps that provide important services to the user are allowed to continue running for any amount of time.

As much as possible, you are encouraged to avoid executing in the background and let your app be suspended. If you find you need to perform background tasks, here are some guidelines for when that is appropriate:

The system keeps suspended apps in memory for as long as possible, removing them only when the amount of free memory gets low. Remaining in memory means that subsequent launches of your app are much faster. At the same time, being suspended means your app does not drain the device’s battery as quickly.

Determining Whether Multitasking Is Available

Apps must be prepared to handle situations where multitasking (and therefore background execution) is not available. Even if the device is running iOS 4 or later, the device may not support multitasking. And multitasking is never available on devices running iOS 3 and earlier. If your app is capable of running on these earlier versions of iOS, it must be prepared to run without multitasking.

If the presence or absence of multitasking changes the way your app behaves, check the multitaskingSupported property of the UIDevice class to determine whether multitasking is available before performing the relevant task. To do this, you must build your app against iOS SDK 4 or later and check to see if the property is available before accessing it, as shown in Listing 3-2. Checking the property allows your app to continue running on earlier versions of iOS that do not support multitasking.

Listing 3-2  Checking for background support in earlier versions of iOS

UIDevice* device = [UIDevice currentDevice];
BOOL backgroundSupported = NO;
if ([device respondsToSelector:@selector(isMultitaskingSupported)])
   backgroundSupported = device.multitaskingSupported;

Executing a Finite-Length Task in the Background

Apps that are transitioning to the background can request an extra amount of time to finish any important last-minute tasks. To request background execution time, call the beginBackgroundTaskWithName:expirationHandler: method of the UIApplication class. If your app moves to the background while the task is in progress, or if your app was already in the background, this method delays the suspension of your app. This can be important if your app is performing some important task, such as writing user data to disk or downloading an important file from a network server.

The way to use the beginBackgroundTaskWithName:expirationHandler: method is to call it before starting the task you want to protect. Every call to this method must be balanced by a corresponding call to the endBackgroundTask: method. Because apps are given only a limited amount of time to finish background tasks, you must call endBackgroundTask: before time expires or the system will terminate your app. You can use the expiration handler you passed to beginBackgroundTaskWithName:expirationHandler: to end the task. (Use the backgroundTimeRemaining property of the app object to see how much time is available.)

Listing 3-3 shows how to start a long-running task when your app transitions to the background. In this example, the request to start a background task includes an expiration handler just in case the task takes too long. The task itself is then submitted to a dispatch queue for asynchronous execution so that the applicationDidEnterBackground: method can return normally. The use of blocks simplifies the code needed to maintain references to any important variables, such as the background task identifier. The bgTask variable is a member variable of the class that stores a pointer to the current background task identifier and is initialized prior to its use in this method.

Listing 3-3  Starting a background task at quit time

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
        // Clean up any unfinished task business by marking where you
        // stopped or ending the task outright.
        [application endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    }];
 
    // Start the long-running task and return immediately.
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
 
        // Do the work associated with the task, preferably in chunks.
 
        [application endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    });
}

In your own expiration handlers, you can include additional code needed to close out your task. However, any code you include must not take too long to execute because, by the time your expiration handler is called, your app is already very close to its time limit. For this reason, perform only minimal cleanup of your state information and end the task.

Scheduling the Delivery of Local Notifications

Notifications are a way for an app that is suspended, is in the background, or is not running to get the user’s attention. Apps can use local notifications to display alerts, play sounds, badge the app’s icon, or a combination of the three. For example, an alarm clock app might use local notifications to play an alarm sound and display an alert to disable the alarm. When a notification is delivered to the user, the user must decide if the information warrants bringing the app back to the foreground. (If the app is already running in the foreground, local notifications are delivered quietly to the app and not to the user.)

To schedule the delivery of a local notification, create an instance of the UILocalNotification class, configure the notification parameters, and schedule it using the methods of the UIApplication class. The local notification object contains information about the type of notification to deliver (sound, alert, or badge) and the time (when applicable) at which to deliver it. The methods of the UIApplication class provide options for delivering notifications immediately or at the scheduled time.

Listing 3-4 shows an example that schedules a single alarm using a date and time that is set by the user. This example configures only one alarm at a time and cancels the previous alarm before scheduling a new one. (Your own apps can have no more than 128 local notifications active at any given time, any of which can be configured to repeat at a specified interval.) The alarm itself consists of an alert box and a sound file that is played if the app is not running or is in the background when the alarm fires. If the app is active and therefore running in the foreground, the app delegate’s application:didReceiveLocalNotification: method is called instead.

Listing 3-4  Scheduling an alarm notification

- (void)scheduleAlarmForDate:(NSDate*)theDate
{
    UIApplication* app = [UIApplication sharedApplication];
    NSArray*    oldNotifications = [app scheduledLocalNotifications];
 
    // Clear out the old notification before scheduling a new one.
    if ([oldNotifications count] > 0)
        [app cancelAllLocalNotifications];
 
    // Create a new notification.
    UILocalNotification* alarm = [[UILocalNotification alloc] init];
    if (alarm)
    {
        alarm.fireDate = theDate;
        alarm.timeZone = [NSTimeZone defaultTimeZone];
        alarm.repeatInterval = 0;
        alarm.soundName = @"alarmsound.caf";
        alarm.alertBody = @"Time to wake up!";
 
        [app scheduleLocalNotification:alarm];
    }
}

Sound files used with local notifications have the same requirements as those used for push notifications. Custom sound files must be located inside your app’s main bundle and support one of the following formats: Linear PCM, MA4, µ-Law, or a-Law. You can also specify the sound name default to play the default alert sound for the device. When the notification is sent and the sound is played, the system also triggers a vibration on devices that support it.

You can cancel scheduled notifications or get a list of notifications using the methods of the UIApplication class. For more information about these methods, see UIApplication Class Reference. For additional information about configuring local notifications, see Local and Push Notification Programming Guide.

Implementing Long-Running Background Tasks

For tasks that require more execution time to implement, you must request specific permissions to run them in the background without their being suspended. In iOS, only specific app types are allowed to run in the background:

  • Apps that play audible content to the user while in the background, such as a music player app

  • Apps that record audio content while in the background.

  • Apps that keep users informed of their location at all times, such as a navigation app

  • Apps that support Voice over Internet Protocol (VoIP)

  • Apps that need to download and process new content regularly

  • Apps that receive regular updates from external accessories

Apps that implement these services must declare the services they support and use system frameworks to implement the relevant aspects of those services. Declaring the services lets the system know which services you use, but in some cases it is the system frameworks that actually prevent your application from being suspended.

Declaring Your App’s Supported Background Tasks

Support for some types of background execution must be declared in advance by the app that uses them. In Xcode 5 and later, you declare the background modes your app supports from the Capabilities tab of your project settings. Enabling the Background Modes option adds the UIBackgroundModes key to your app’s Info.plist file. Selecting one or more checkboxes adds the corresponding background mode values to that key. Table 3-4 lists the background modes you can specify and the values that Xcode assigns to the UIBackgroundModes key in your app’s Info.plist file.

Table 3-4  Background modes for apps

Xcode mode

UIBackgroundModes value

Description

Audio

audio

The app plays audible content to the user or records audio while in the background. (This content includes streaming audio or video content using AirPlay.)

Location updates

location

The app keeps users informed of their location, even while it is running in the background.

Voice over IP

voip

The app provides the ability for the user to make phone calls using an Internet connection.

Newsstand downloads

newsstand-content

The app is a Newsstand app that downloads and processes magazine or newspaper content in the background.

External accessory communication

external-accessory

The app works with a hardware accessory that needs to deliver updates on a regular schedule through the External Accessory framework.

Bluetooth networking

bluetooth-central

The app works with a Bluetooth accessory that needs to deliver updates on a regular schedule through the Core Bluetooth framework.

Bluetooth data sharing

bluetooth-peripheral

The app supports Bluetooth communication in peripheral mode through the Core Bluetooth framework.

Using this mode requires user authorization; for more information, see “Best Practices for Maintaining User Privacy.”

Background fetch

fetch

The app regularly downloads and processes small amounts of content from the network.

Remote notifications

remote-notification

The app’s response to a push notification includes optionally downloading content related to the notification. The purpose of downloading the content in the background is to incorporate it and be able to present it immediately when the user responds to the push notification.

Each of the preceding modes lets the system know that your app should be woken up or launched at appropriate times to respond to relevant events. For example, an app that begins playing music and then moves to the background still needs execution time to fill the audio output buffers. Enabling the Audio mode tells the system frameworks that they should continue to make the necessary callbacks to the app at appropriate intervals. If the app does not select this mode, any audio being played or recorded by the app stops when the app moves to the background.

Tracking the User’s Location

There are several ways to track the user’s location in the background, most of which do not actually require your app to run continuously in the background:

  • The significant-change location service (Recommended)

  • Foreground-only location services

  • Background location services

The significant-change location service is highly recommended for apps that do not need high-precision location data. With this service, location updates are generated only when the user’s location changes significantly; thus, it is ideal for social apps or apps that provide the user with noncritical, location-relevant information. If the app is suspended when an update occurs, the system wakes it up in the background to handle the update. If the app starts this service and is then terminated, the system relaunches the app automatically when a new location becomes available. This service is available in iOS 4 and later, and it is available only on devices that contain a cellular radio.

The foreground-only and background location services both use the standard location Core Location service to retrieve location data. The only difference is that the foreground-only location services stop delivering updates if the app is ever suspended, which is likely to happen if the app does not support other background services or tasks. Foreground-only location services are intended for apps that only need location data while they are in the foreground.

You enable location support from the Background modes section of the Capabilities tab in your Xcode project. (You can also enable this support by including the UIBackgroundModes key with the location value in your app’s Info.plist file.) Enabling this mode does not prevent the system from suspending the app, but it does tell the system that it should wake up the app whenever there is new location data to deliver. Thus, this key effectively lets the app run in the background to process location updates whenever they occur.

For information about how to use each of the different location services in your app, see Location and Maps Programming Guide.

Playing and Recording Background Audio

An app that plays or records audio continuously (even while the app is running in the background) can register to perform those tasks in the background. You enable audio support from the Background modes section of the Capabilities tab in your Xcode project. (You can also enable this support by including the UIBackgroundModes key with the audio value in your app’s Info.plist file.) Apps that play audio content in the background must play audible content and not silence.

Typical examples of background audio apps include:

  • Music player apps

  • Audio recording apps

  • Apps that support audio or video playback over AirPlay

  • VoIP apps

When the UIBackgroundModes key contains the audio value, the system’s media frameworks automatically prevent the corresponding app from being suspended when it moves to the background. As long as it is playing audio or video content or recording audio content, the app continues to run in the background. However, if recording or playback stops, the system suspends the app.

You can use any of the system audio frameworks to work with background audio content, and the process for using those frameworks is unchanged. (For video playback over AirPlay, you can use the Media Player or AV Foundation framework to present your video.) Because your app is not suspended while playing media files, callbacks operate normally while your app is in the background. In your callbacks, though, you should do only the work necessary to provide data for playback. For example, a streaming audio app would need to download the music stream data from its server and push the current audio samples out for playback. Apps should not perform any extraneous tasks that are unrelated to playback.

Because more than one app may support audio, the system determines which app is allowed to play or record audio at any given time. The foreground app always has priority for audio operations. It is possible for more than one background app to be allowed to play audio and such determinations are based on the configuration of each app’s audio session objects. You should always configure your app’s audio session object appropriately and work carefully with the system frameworks to handle interruptions and other types of audio-related notifications. For information on how to configure audio session objects for background execution, see Audio Session Programming Guide.

Implementing a VoIP App

A Voice over Internet Protocol (VoIP) app allows the user to make phone calls using an Internet connection instead of the device’s cellular service. Such an app needs to maintain a persistent network connection to its associated service so that it can receive incoming calls and other relevant data. Rather than keep VoIP apps awake all the time, the system allows them to be suspended and provides facilities for monitoring their sockets for them. When incoming traffic is detected, the system wakes up the VoIP app and returns control of its sockets to it.

To configure a VoIP app, you must do the following:

  1. Enable support for Voice over IP from the Background modes section of the Capabilities tab in your Xcode project. (You can also enable this support by including the UIBackgroundModes key with the voip value in your app’s Info.plist file.)

  2. Configure one of the app’s sockets for VoIP usage.

  3. Before moving to the background, call the setKeepAliveTimeout:handler: method to install a handler to be executed periodically. Your app can use this handler to maintain its service connection.

  4. Configure your audio session to handle transitions to and from active use.

Including the voip value in the UIBackgroundModes key lets the system know that it should allow the app to run in the background as needed to manage its network sockets. An app with this key is also relaunched in the background immediately after system boot to ensure that the VoIP services are always available.

Most VoIP apps also need to be configured as background audio apps to deliver audio while in the background. Therefore, you should include both the audio and voip values to the UIBackgroundModes key. If you do not do this, your app cannot play or record audio while it is in the background. For more information about the UIBackgroundModes key, see Information Property List Key Reference.

For specific information about the steps you must take to implement a VoIP app, see “Tips for Developing a VoIP App.”

Fetching Small Amounts of Content Regularly

In iOS 7 and later, an app that retrieves content regularly from the network can ask the system for background execution time to check for new content. You enable support for background fetches from the Background modes section of the Capabilities tab in your Xcode project. (You can also enable this support by including the UIBackgroundModes key with the fetch value in your app’s Info.plist file.) At appropriate times, the system gives background execution time to the apps that support this background mode, launching the app directly into the background if needed. The app object calls the application:performFetchWithCompletionHandler: method of its app delegate to let you know when execution time is available.

When the application:performFetchWithCompletionHandler: method of your delegate is called, use that method to check for new content and to download that content if it is available. When your downloads are complete, execute the provided completion handler block, passing a result that indicates whether content was available. Executing this block tells the system that it can move your app back to the suspended state and evaluate its power usage. Apps that download small amounts of content quickly and accurately reflect when they had content to download are more likely to receive execution time in the future than apps that take longer to download their content.

When downloading any content, it is recommended that you use the NSURLSession class to initiate and manage your downloads. For information about how to use this class to manage upload and download tasks, see URL Loading System Programming Guide.

Using Push Notifications to Initiate a Download

If your app uses push notifications to notify the user of the availability of new content, you can use those push notifications to trigger the download of that content in iOS 7 and later. You enable support for remote notifications from the Background modes section of the Capabilities tab in your Xcode project. (You can also enable this support by including the UIBackgroundModes key with the remote-notification value in your app’s Info.plist file.) When this value is present and a push notification arrives on a device, the system sends the notification to your app (launching it if needed) and gives it a few moments to process the notification before displaying anything to the user. You can use those few moments to download content related to the push notification and be ready to display it to the user.

To receive push notifications, implement the application:didReceiveRemoteNotification:fetchCompletionHandler: method in your app delegate object. In your implementation of this method, download your content and execute the provided completion block when those downloads are complete.

When downloading any content, it is recommended that you use the NSURLSession class to initiate and manage your downloads. For information about how to use this class to manage upload and download tasks, see URL Loading System Programming Guide.

Downloading Newsstand Content in the Background

A Newsstand app that downloads new magazine or newspaper issues can register to perform those downloads in the background. You enable support for newsstand downloads from the Background modes section of the Capabilities tab in your Xcode project. (You can also enable this support by including the UIBackgroundModes key with the newsstand-content value in your app’s Info.plist file.) When this key is present, the system launches your app, if it is not already running, so that it can initiate the downloading of the new issue.

When you use the Newsstand Kit framework to initiate a download, the system handles the download process for your app. The system continues to download the file even if your app is suspended or terminated. When the download operation is complete, the system transfers the file to your app sandbox and notifies your app. If the app is not running, this notification wakes it up and gives it a chance to process the newly downloaded file. If there are errors during the download process, your app is similarly woken up to handle them.

For information about how to download content using the Newsstand Kit framework, see Newsstand Kit Framework Reference.

Communicating with an External Accessory

Apps that work with external accessories can ask to be woken up if the accessory delivers an update when the app is suspended. This support is important for some types of accessories that deliver data at regular intervals, such as heart-rate monitors. You enable support for external accessory communication from the Background modes section of the Capabilities tab in your Xcode project. (You can also enable this support by including the UIBackgroundModes key with the external-accessory value in your app’s Info.plist file.) When you enable this mode, the external accessory framework does not close active sessions with accessories. (In iOS 4 and earlier, these sessions are closed automatically when the app is suspended.) When new data arrives from the accessory, the framework wakes your app so that it can process that data. The system also wakes the app to process accessory connection and disconnection notifications.

Any app that supports the background processing of accessory updates must follow a few basic guidelines:

  • Apps must provide an interface that allows the user to start and stop the delivery of accessory update events. That interface should then open or close the accessory session as appropriate.

  • Upon being woken up, the app has around 10 seconds to process the data. Ideally, it should process the data as fast as possible and allow itself to be suspended again. However, if more time is needed, the app can use the beginBackgroundTaskWithExpirationHandler: method to request additional time; it should do so only when absolutely necessary, though.

Communicating with a Bluetooth Accessory

Apps that work with Bluetooth peripherals can ask to be woken up if the peripheral delivers an update when the app is suspended. This support is important for Bluetooth-le accessories that deliver data at regular intervals, such as a Bluetooth heart rate belt. You enable support for using bluetooth accessories from the Background modes section of the Capabilities tab in your Xcode project. (You can also enable this support by including the UIBackgroundModes key with the bluetooth-central value in your app’s Info.plist file.) When you enable this mode, the Core Bluetooth framework keeps open any active sessions for the corresponding peripheral. In addition, new data arriving from the peripheral causes the system to wake up the app so that it can process the data. The system also wakes up the app to process accessory connection and disconnection notifications.

In iOS 6, an app can also operate in peripheral mode with Bluetooth accessories. To act as a Bluetooth accessory, you must enable support for that mode from the Background modes section of the Capabilities tab in your Xcode project. (You can also enable this support by including the UIBackgroundModes key with the bluetooth-peripheral value in your app’s Info.plist file.) Enabling this mode lets the Core Bluetooth framework wake the app up briefly in the background so that it can handle accessory-related requests. Apps woken up for these events should process them and return as quickly as possible so that the app can be suspended again.

Any app that supports the background processing of Bluetooth data must be session-based and follow a few basic guidelines:

  • Apps must provide an interface that allows the user to start and stop the delivery of Bluetooth events. That interface should then open or close the session as appropriate.

  • Upon being woken up, the app has around 10 seconds to process the data. Ideally, it should process the data as fast as possible and allow itself to be suspended again. However, if more time is needed, the app can use the beginBackgroundTaskWithExpirationHandler: method to request additional time; it should do so only when absolutely necessary, though.

Being a Responsible Background App

The foreground app always has precedence over background apps when it comes to the use of system resources and hardware. Apps running in the background need to be prepared for this discrepancy and adjust their behavior when running in the background. Specifically, apps moving to the background should follow these guidelines:

  • Do not make any OpenGL ES calls from your code. You must not create an EAGLContext object or issue any OpenGL ES drawing commands of any kind while running in the background. Using these calls causes your app to be killed immediately. Apps must also ensure that any previously submitted commands have completed before moving to the background. For information about how to handle OpenGL ES when moving to and from the background, see “Implementing a Multitasking-aware OpenGL ES Application” in OpenGL ES Programming Guide for iOS.

  • Cancel any Bonjour-related services before being suspended. When your app moves to the background, and before it is suspended, it should unregister from Bonjour and close listening sockets associated with any network services. A suspended app cannot respond to incoming service requests anyway. Closing out those services prevents them from appearing to be available when they actually are not. If you do not close out Bonjour services yourself, the system closes out those services automatically when your app is suspended.

  • Be prepared to handle connection failures in your network-based sockets. The system may tear down socket connections while your app is suspended for any number of reasons. As long as your socket-based code is prepared for other types of network failures, such as a lost signal or network transition, this should not lead to any unusual problems. When your app resumes, if it encounters a failure upon using a socket, simply reestablish the connection.

  • Save your app state before moving to the background. During low-memory conditions, background apps may be purged from memory to free up space. Suspended apps are purged first, and no notice is given to the app before it is purged. As a result, apps should take advantage of the state preservation mechanism in iOS 6 and later to save their interface state to disk. For information about how to support this feature, see “State Preservation and Restoration.”

  • Remove strong references to unneeded objects when moving to the background. If your app maintains a large in-memory cache of objects (especially images), remove all strong references to those caches when moving to the background. For more information, see “Memory Usage for Background Apps.”

  • Stop using shared system resources before being suspended. Apps that interact with shared system resources such as the Address Book or calendar databases should stop using those resources before being suspended. Priority for such resources always goes to the foreground app. When your app is suspended, if it is found to be using a shared resource, the app is killed.

  • Avoid updating your windows and views. While in the background, your app’s windows and views are not visible, so you should not try to update them. Although creating and manipulating window and view objects in the background does not cause your app to be killed, consider postponing this work until you return to the foreground.

  • Respond to connect and disconnect notifications for external accessories. For apps that communicate with external accessories, the system automatically sends a disconnection notification when the app moves to the background. The app must register for this notification and use it to close out the current accessory session. When the app moves back to the foreground, a matching connection notification is sent, giving the app a chance to reconnect. For more information on handling accessory connection and disconnection notifications, see External Accessory Programming Topics.

  • Clean up resources for active alerts when moving to the background. In order to preserve context when switching between apps, the system does not automatically dismiss action sheets (UIActionSheet) or alert views (UIAlertView) when your app moves to the background. It is up to you to provide the appropriate cleanup behavior prior to moving to the background. For example, you might want to cancel the action sheet or alert view programmatically or save enough contextual information to restore the view later (in cases where your app is terminated).

    For apps linked against a version of iOS earlier than 4.0, action sheets and alerts are still dismissed at quit time so that your app’s cancellation handler has a chance to run.

  • Remove sensitive information from views before moving to the background. When an app transitions to the background, the system takes a snapshot of the app’s main window, which it then presents briefly when transitioning your app back to the foreground. Before returning from your applicationDidEnterBackground: method, you should hide or obscure passwords and other sensitive personal information that might be captured as part of the snapshot.

  • Do minimal work while running in the background. The execution time given to background apps is more constrained than the amount of time given to the foreground app. Apps that spend too much time executing in the background can be throttled back by the system or terminated.

If you are implementing a background audio app, or any other type of app that is allowed to run in the background, your app responds to incoming messages in the usual way. In other words, the system may notify your app of low-memory warnings when they occur. And in situations where the system needs to terminate apps to free even more memory, the app calls its delegate’s applicationWillTerminate: method to perform any final tasks before exiting.

Opting out of Background Execution

If you do not want your app to run in the background at all, you can explicitly opt out of background by adding the UIApplicationExitsOnSuspend key (with the value YES) to your app’s Info.plist file. When an app opts out, it cycles between the not-running, inactive, and active states and never enters the background or suspended states. When the user presses the Home button to quit the app, the applicationWillTerminate: method of the app delegate is called and the app has approximately 5 seconds to clean up and exit before it is terminated and moved back to the not-running state.

Opting out of background execution is strongly discouraged but may be the preferred option under certain conditions. Specifically, if coding for the background adds significant complexity to your app, terminating the app might be a simpler solution. Also, if your app consumes a large amount of memory and cannot easily release any of it, the system might kill your app quickly anyway to make room for other apps. Thus, opting to terminate, instead of switching to the background, might yield the same results and save you development time and effort.

For more information about the keys you can include in your app’s Info.plist file, see Information Property List Key Reference.

Concurrency and Secondary Threads

The system creates your app’s main thread but your app can create additional threads, as needed, to perform other tasks. The preferred way to create threads is to let the system do it for you by using Grand Central Dispatch queues and operation queues. Both types of queue provide an asynchronous execution model for tasks that you define. When you submit a task to a queue, the system spins up a thread and executes your task on that thread. Letting the system manage the threads simplifies your code and allows the system to manage the threads in the most efficient way available.

You should use queues whenever possible to move work off of your app’s main thread. Because the main thread is responsible for processing touch and drawing events, you should never perform lengthy tasks on it. For example, you should never wait for a network response on your app’s main thread. It is much better to make the request asynchronously using a queue and process the results when they arrive.

Another good time to move tasks to secondary threads is launch time. Launched apps have a limited amount of time (around 5 seconds) to do their initialization and start processing events. If you have launch-time tasks that can be deferred or executed on a secondary thread, you should move them off the main thread right away and use the main thread only to present your user interface and start handling events.

For more information about using dispatch and operation queues to execute tasks, see Concurrency Programming Guide.