Hour 22. Building Background-Ready Applications


What You’ll Learn in This Hour:

Image How iOS supports background tasks

Image What types of background operations are supported

Image How to disable backgrounding

Image Ways to execute code in the background

Image How to add 3D Touch Quick Actions


“Real multitasking” claims the commercial for a competitor’s tablet. “Unlike Apple, you can run multiple things at once,” chides another ad. As a developer and a fan of iOS, I’ve found these threads amusing in their naiveté and somewhat confusing. iDevices have always run multiple applications simultaneously in the background, but they were limited to Apple’s applications. This restriction has been to preserve the user experience of the device instead of letting it bog down to the point of being unusable. Rather than an “anything goes” approach, Apple has taken steps to ensure that iOS devices remain responsive at all times.

In recent years, Apple has dramatically opened up background processing with many new capabilities. Unlike the competitors, however, Apple has been cautious in its backgrounding approach—preventing a single process from completely taking over the operating system. In this hour’s lesson, you learn several of the multitasking techniques that you can implement in your applications.

Understanding iOS Backgrounding

As you’ve built the tutorials in this book, you might have noticed that when you quit the applications on your device or in the iOS Simulator, they still show up in the iOS task manager, and unless you manually stop them, they tend to pick up right where they left off. The reason for this is that projects are background ready as soon as you click the Run button. That doesn’t mean that they will run in the background, just that they’re aware of the background features and will take advantage with a little bit of help.

Before we examine how to enable backgrounding (also called multitasking) in our projects, let’s first identify exactly what it means to be a background-aware application, starting with the types of backgrounding supported and then the application life cycle methods you can tie into.


Split-Screen Multitasking Is Free

We’re going to be talking about all sorts of ways that your application can interact with iOS to perform actions or present options without being open on your screen. Split-screen multitasking, introduced in iOS 9 and available on Apple’s current shipping iPad line, doesn’t require special programming to function. This type of multitasking requires that you implement an application using Size Classes and Auto Layout. If your application can properly resize to different screen sizes, it can split-screen multitask. In fact, you must explicitly add the Boolean key UIRequiresFullScreen with a value of true to your project’s Info.plist file if you want to opt out of split-screen multitasking.

Assuming you do want your app to be used in a split-screen mode, you must also be aware that it won’t have full control over the device’s resources. In other words, if you follow good programming practices and use memory and device resources efficiently, your app will work wonderfully in Apple’s new split-screen environment.


Types of Backgrounding

We explore six types of background operations in iOS: application suspension, local notifications, task-specific background processing, task completion, background fetches, and 3D Touch Quick Actions.

Suspension

When an application is suspended, it ceases executing code but is preserved exactly as the user left it. When the user returns to the application, it appears to have been running the whole time. In reality, all tasks are stopped, keeping the app from using up your device’s resources. Any application that you compile will, by default, support background suspension. You should still handle cleanup in the application if it is about to be suspended (see the “Background-Aware Application Life Cycle Methods” section, later in this hour), but beyond that, it “just works.”

In addition to performing cleanup as an application is being suspended, it is your responsibility to recover from a background suspended state and update anything in the application that should have changed while it was suspended (time/date changes and so on).

Local Notifications

The second type of background processing is the scheduling of local notifications (UILocalNotification). If you’ve ever experienced a push notification, local notifications are the same but are generated by the applications that you write. An application, while running, can schedule notifications, with sounds and app icon badges to appear at a point in time in the future.

Before this can happen, however, you must request permission, using the UIApplication method registerUserNotificationSettings. For example, to request that my app be able to generate notifications that include sound, icon badges numbers, and alert boxes, I would add the following code to my application:didFinishLaunchingWithOptions method in AppDelegate.swift:

let notificationTypes:UIUserNotificationType =
    [UIUserNotificationType.Sound,
     UIUserNotificationType.Badge,
     UIUserNotificationType.Alert]

let notificationSettings =
    UIUserNotificationSettings(forTypes: notificationTypes,
        categories: nil)

UIApplication.sharedApplication()
    .registerUserNotificationSettings(notificationSettings)

Once permission has been requested, you can generate a notification. The following code initializes a notification (UILocationNotification), configures it to appear in 5 minutes (300 seconds from “now”), and then uses the application’s scheduleLocalNotification method to complete the scheduling:

let scheduledAlert: UILocalNotification = UILocalNotification()
scheduledAlert.fireDate=NSDate(timeIntervalSinceNow: 300)
scheduledAlert.timeZone=NSTimeZone.defaultTimeZone()
scheduledAlert.alertBody="Hey, remember me?"

These notifications, when invoked by iOS, can show a message, play a sound, and even update your application’s notification badge. They cannot, however, execute arbitrary application code. In fact, it is likely that you will simply allow iOS to suspend your application after registering your local notifications. A user who receives a notification can click the View button in the notification window to return to your application.


Note

iOS allows remote notifications to be sent to a device to trigger applications to activate and begin processing. This can be useful for asking an application to retrieve new information when it becomes available (having it instantly appear when the user next opens the app). Using remote notifications requires you to have a server infrastructure set up to track and communicate with your user’s iOS devices (which, unfortunately, is beyond the scope of this book). Learn more by reading the document Local and Push Notification Programming Guide found in the Xcode help system.


Task-Specific Background Processing

Before Apple decided to implement background processing, it did some research on how users worked with their devices. Apple found that people needed specific types of background processing. First, they needed audio to continue playing in the background; this is necessary for applications like Pandora. Next, location-aware software needed to update itself in the background so that users continued to receive navigation feedback. Finally, Voice over IP (VoIP) applications like Skype needed to operate in the background to handle incoming calls.

These three types of tasks are handled uniquely and elegantly in iOS. By declaring that your application requires one of these types of background processing, you can, in many cases, enable your application to continue running with little alteration. To declare your application capable of supporting any (or all) of these tasks, you will add the Required Background Modes (UIBackgroundModes) key to the project’s plist file and then add features (background audio, picture-in-picture support, and so on) that you’ll be supporting. Xcode even lets you do this in a cool point-and-click way, so you can avoid the manually editing Info.plist. Don’t worry; you still have plenty of opportunities to experience the nightmare of plist editing elsewhere this hour.

Task Completion for Long-Running Tasks

The fourth type of backgrounding that we’ll use is task completion. Using task-completion methods, you can “mark” the tasks in your application that will need to finish before the application can be safely suspended (file upload/downloads, massive calculations, and so on).

For example, to mark the beginning of a long-running task, first declare an identifier for the specific task:

var myLongTask: UIBackgroundTaskIdentifier!

Then use the application’s beginBackgroundTaskWithExpirationHandler method to tell iOS that you’re starting a piece of code that can continue to run in the background:

myLongTask =
    UIApplication.sharedApplication()
        .beginBackgroundTaskWithExpirationHandler({ () -> Void in
        // If you're worried about exceeding 10 minutes, handle it here
    })

And finally, mark the end of the long-running task with the application endBackgroundTask method:

UIApplication.sharedApplication().endBackgroundTask(myLongTask)

Each task you mark will have roughly 10 minutes (total) to complete its actions, which is plenty of time for most uses. After the time completes, the application is suspended and treated like any other suspended application.

Background Fetches

The fifth and final multitasking feature we review this hour is background fetches. Using the background fetch feature, your application can periodically launch and execute a method that retrieves and processes update data. Scheduling of the updates happens automatically based on a user’s usage. If a user starts an app and uses it each morning, iOS will make sure that a background fetch occurs before the time the user is typically using it. In addition, iOS prevents multiple applications from attempting background fetches at the same time, thus keeping the device responsive even if background processing is taking place.

As a developer, you need to do just two things to implement background updates. First, you must edit the application:didFinishLaunchingWithOptions method in the application delegate (AppDelegate.swift) to set the minimum amount of time between fetches. This consists of a single line (broken here for readability):

UIApplication.sharedApplication().setMinimumBackgroundFetchInterval(
    UIApplicationBackgroundFetchIntervalMinimum)

The constant UIApplicationBackgroundFetchIntervalMinimum tells the application that you want updates to happen as often as they can. If you have a specific interval in mind, you can provide a number in seconds instead. You aren’t guaranteed an interval by iOS; it is intended to be scheduled around a user’s activities.

The second step is to implement the background fetch itself. To do this, you add the following method to the application delegate:

func application(application: UIApplication,
    performFetchWithCompletionHandler completionHandler:
    (UIBackgroundFetchResult) -> Void) {

    // Do something useful here

    //Indicate completion
    completionHandler(UIBackgroundFetchResult.NewData)

}

This method has 30 seconds to execute and perform the background fetch. When completed, it should call completionHandler(UIBackgroundFetchResult.NewData) to indicate success.

3D Touch Quick Actions

What do you do when you launch an application? Chances are, you perform a series of actions each time it starts up—choose to open a new document, switch to a new mode, and so on. With 3D Touch-enabled devices, you can enable your users to configure your application, before it ever appears onscreen, by firmly pressing the app icon, as shown in Figure 22.1.

Image

FIGURE 22.1 3D Touch Quick Actions give users access to application functionality before the application launches, or when it is in the background.

3D Touch Quick Actions themselves do not execute code in the background, but instead they present a limited interface to your application while it isn’t running.

There are two parts to implementing Quick Actions. The first is to define the actions (and associated icons, if desired) in the project’s Info.plist, and the second is to check for those actions in an AppDelegate.swift method: application:performActionForShortcutItem.

To add the Quick Actions to the Info.plist file, edit the file and add a new property with the key UIApplicationShortcutItems of the type Array. Within this array, you add items for each Quick Action that you want to display. Each item must be of the type Dictionary and typically contains up to four keys, all of the type String:

UIApplicationShortcutItemTitle: The title that should be displayed to the user.

UIApplicationShortcutItemSubtitle: An optional subtitle that can be displayed below the title.

UIApplicationShortcutItemType: A unique string that your application will receive when the user chooses that action.

UIApplicationShortcutItemIconFile: The name of an image within the Assets.xcassets library that will be displayed alongside the title. This is optional, but highly recommended.


Note

Additional 3D Touch Quick Actions settings are available for further fine-tuning your menus. Learn more by reading the document Adopting 3D Touch on iPhone: 3D Touch APIs found in the Xcode Documentation.


After the quick actions have been defined in info.plist, they’ll be displayed automatically when the app icon is pressed on a 3D Touch-enabled device. To react to an action being chosen, you’ll implement application:performActionForShortcutItem in AppDelegate.swift.

For example, to react to a Quick Action with a UIApplicationShortcutItemType defined as doStuff, you might use something like this:

func application(application: UIApplication,
    performActionForShortcutItem shortcutItem: UIApplicationShortcutItem,
    completionHandler: (Bool) -> Void) {
    var success: Bool = false
    if shortcutItem.type == "doStuff" {
        // The "doStuff" action was chosen
        success = true
    } else {
        // Something went wrong
        success = false
    }
    completionHandler(success)
}

The method checks shortcutItem.type to see whether it matches one of the defined Quick Actions. If it does, your custom code is executed. As a final step, you must call completionHandler with a Bool (Boolean) value to indicate success or failure of the action.


Tip: Want Pretty Quick Action Icons? Use a Template

Quick Action icons should be transparent line drawings, similar to those used with tab bar items. To get you started on the right path, Apple has published templates for creating your own icons. You can download these downloaded from https://developer.apple.com/design/downloads/Quick-Action-Guides.zip


Background-Aware Application Life Cycle Methods

In Hour 4, “Inside Cocoa Touch,” you started learning about the application life cycle, shown in Figure 22.2. You learned that applications should clean up after themselves in the applicationDidEnterBackground delegate method. This replaces applicationWillTerminate in earlier versions of the OS, or as you’ll learn shortly, in applications that you’ve specifically marked as not capable (or necessary) to run in the background.

Image

FIGURE 22.2 The iOS application life cycle.

In addition to applicationDidEnterBackground, you should implement several other methods to be a proper background-aware iOS citizen. For many small applications, you do not need to do anything with these other than leave them as is in the application delegate. As your projects increase in complexity, however, make sure that your apps move cleanly from the foreground to background (and vice versa), avoiding potential data corruption and creating a seamless user experience.


Caution: Your Application Can Terminate at Any Time

It is important to understand that iOS can terminate your applications, even if they’re backgrounded, if it decides that the device is running low on resources. You can expect that your applications will be fine, but plan for a scenario where they are forced to quit unexpectedly.



Note

If you’re interested in implementing background fetching, you can take advantage of the methods we discuss here, but they aren’t necessary. You’re only required to set the minimum fetch time and then to define a method for handling the data fetching itself. We’ll implement some simple background fetching later this hour.


Apple expects to see the following methods in your background-aware apps:

Image application:didFinishLaunchingWithOptions: Called when your application first launches. If your application is terminated while suspended or purged from memory, it needs to restore its previous state manually. (You did save it to your user’s preferences, right?)

Image applicationDidBecomeActive: Called when an application launches or returns to the foreground from the background. This method can be used to restart processes and update the user interface, if needed.

Image applicationWillResignActive: Invoked when the application is requested to move to the background or to quit. This method should be used to prepare the application for moving into a background state, if needed.

Image applicationDidEnterBackground: Called when the application has become a background application. This largely replaces applicationWillTerminate, which was used when an application quit. You should handle all final cleanup work in this method. You may also use it to start long-running tasks and use task-completion backgrounding to finish them.

Image applicationWillEnterForeground: Called when an application returns to an active state after being backgrounded.

Image applicationWillTerminate: Invoked when an application on a nonmultitasking version of iOS is asked to quit or when iOS determines that it needs to shut down an actively running background application.

Method stubs for all of these exist in your application delegate file. If your application needs additional setup or teardown work, just add the code to the existing methods. As you’ll see shortly, many applications, such as the majority of those in this book, require few changes.

Now that understand the background-related methods and types of background processing available to you, let’s look at how you can implement them. To do this, we reuse tutorials that we’ve built throughout the book (with two exceptions). We do not cover how these tutorials were built, so be sure to refer to the earlier hours if you have questions on the core functionality of the applications.

Disabling Backgrounding

We start with the exact opposite of enabling backgrounding: disabling it. If you think about it, many different “diversion” apps don’t need to support background suspension or processing. These are apps that you use and then quit. They don’t need to hang around in your task manager afterward.

For example, consider the HelloNoun application in Hour 6, “Model-View-Controller Application Design.” There’s no reason that the user experience would be negatively affected if the application were to start from scratch each time you ran it. To implement this change in the project, follow these steps:

1. Open the project in which you want to disable backgrounding (such as HelloNoun).

2. Choose the main project group and click the HelloNoun target, and then expand the Custom iOS Target Properties under the Info tab. Or open the project’s plist file (Info.plist).

3. Add an additional row to the displayed property list (right-click the list, choose Add Row), selecting Application Does Not Run in Background (UIApplicationExitsOnSuspend) from the Key pop-up menu.

4. Choose Yes from the pop-up menu at the right side of the Value column, as shown in Figure 22.3.

Image

FIGURE 22.3 Add the Application Does Not Run in Background (UIApplicationExitsOnSuspend) key to the project.


Note

By default, the plist editor shows the “developer friendly” names for plist entries. To see the underlying keys/values, right-click on the list and choose Show Raw Keys/Values from the menu.


Run the application on your device or in the iOS Simulator. When you exit the application with the Home button, it will not be suspended, and it will restart fresh when you launch it the next time.

Handling Background Suspension

In the second tutorial, we handle background suspension. As previously noted, you don’t have to do anything to support this other than build your project with the iOS development tools. That said, we use this example as an opportunity to prompt users when they return to the application after it was backgrounded.

For this example, we update the ImageHop application from Hour 8, “Handling Images, Animation, Sliders, and Steppers.” It is conceivable (work with me here, folks) that a user will want to start the bunny hopping, exit the application, and then return to exactly where it was at some time in the future.

To alert the user when the application returns from suspension, we edit the application delegate method applicationWillEnterForeground. Recall that this method is invoked only when an application is returning from a backgrounded state. Open AppDelegate.swift and implement the method, as shown in Listing 22.1.

LISTING 22.1 Implementing the applicationWillEnterForeground Method


 1: func applicationWillEnterForeground(application: UIApplication) {
 2:     let alertController = UIAlertController(title: "Yawn!",
 3:         message: "Was I asleep?",
 4:         preferredStyle: UIAlertControllerStyle.Alert)
 5:
 6:     let defaultAction = UIAlertAction(title: "Welcome Back",
 7:         style: UIAlertActionStyle.Cancel,
 8:         handler: nil)
 9:
10:     alertController.addAction(defaultAction)
11:     self.window!.rootViewController!
12:         .presentViewController(alertController, animated: true,
13:             completion: nil)
14: }


Within the method, we declare, configure, and show an alert controller, exactly as we did in the Getting Attention tutorial in Hour 10, “Getting the User’s Attention.” After updating the code, run the application. Start the ImageHop animation, and then use the Home button to background the app.

After waiting a few seconds (just for good measure), open ImageHop again using the task manager or its application icon (not with Xcode’s Run). When the application returns to the foreground, it should pick up exactly where it left off and present you with the alert shown in Figure 22.4.

Image

FIGURE 22.4 The applicationWillEnterForeground method is used to display an alert upon returning from the background.

Implementing Local Notifications

Earlier in this lesson, you saw a short snippet of the code necessary to generate a local notification (UILocalNotification). As it turns out, there’s not much more you need beyond those few lines. To demonstrate the use of local notifications, we update Hour 10’s doAlert method. Instead of just displaying an alert, it also shows a notification 5 minutes later and then schedules local notifications to occur every day thereafter.

Requesting Authorization for Notifications

Before displaying a notification, your application must request authorization. Without proper permission, notifications will fail silently (that is, your code will run, but nothing will be displayed).

Open the GettingAttention application and edit AppDelegate.swift file, updating the application:didFinishLaunchingWithOptions method, as shown in Listing 22.2.

LISTING 22.2 Requesting Notification Authorization


 1: func application(application:UIApplication,didFinishLaunchingWithOptions
 2:     launchOptions: [NSObject: AnyObject]?) -> Bool {
 3:
 4:     let notificationTypes:UIUserNotificationType =
 5:         [UIUserNotificationType.Sound,
 6:         UIUserNotificationType.Badge,
 7:         UIUserNotificationType.Alert]
 8:
 9:     let notificationSettings =
10:         UIUserNotificationSettings(forTypes: notificationTypes,
11:             categories: nil)
12:
13:     UIApplication.sharedApplication()
14:         .registerUserNotificationSettings(notificationSettings)
15:
16:     return true
17: }


To request authorization to use notifications, you must first define the types of notifications (UIUserNotificationType) you want to display (lines 4–7). In this example, we’re requesting the ability to use any type of notification (sounds, alerts, and badges)—so all the bases are covered. Next, you must create a UIUserNotificationSettings object that includes the types (lines 9–11). Finally, the settings are registered with the application (lines 13–14), which generates an alert and prompts the user for permission.

What’s the takeaway here? Use this code block and you’ll properly request the ability to use any local notifications. Let’s move on to the notifications themselves.

Common Notification Properties

You want to configure several properties when creating notifications. A few of the more interesting of these include the following:

Image applicationIconBadgeNumber: An integer that is displayed on the application icon when the notification is triggered.

Image fireDate: An NSDate object that provides a time in the future for the notification to be triggered.

Image timeZone: The time zone to use for scheduling the notification.

Image repeatInterval: How frequently, if ever, the notification should be repeated.

Image soundName: A string (NSString) containing the name of a sound resource to play when the notification is triggered.

Image alertBody: A string (NSString) containing the message to be displayed to the user.

Creating and Scheduling a Notification

Open the GettingAttention application and edit the doAlert method so that it resembles Listing 22.3. (Bolded lines are additions to the existing method.) Once the code is in place, we walk through it together.

LISTING 22.3 Updating doAlert to Register a Local Notification


 1: @IBAction func doAlert(sender: AnyObject) {
 2:     let scheduledAlert: UILocalNotification = UILocalNotification()
 3:
 4:     UIApplication.sharedApplication().cancelAllLocalNotifications()
 5:     scheduledAlert.applicationIconBadgeNumber=1
 6:     scheduledAlert.fireDate=NSDate(timeIntervalSinceNow: 300)
 7:     scheduledAlert.timeZone=NSTimeZone.defaultTimeZone()
 8:     scheduledAlert.repeatInterval=NSCalendarUnit.Day
 9:     scheduledAlert.soundName="soundeffect.wav"
10:     scheduledAlert.alertBody="I'd like to get your attention again!"
11:     UIApplication.sharedApplication()
12:         .scheduleLocalNotification(scheduledAlert)
13:
14:     let alertController =
15:         UIAlertController(title: "Alert Me Button Selected",
16:         message: "I need your attention NOW!",
17:         preferredStyle: UIAlertControllerStyle.Alert)
18:
19:     let defaultAction = UIAlertAction(title: "Ok",
20:         style: UIAlertActionStyle.Cancel,
21:         handler: nil)
22:
23:     alertController.addAction(defaultAction)
24:     presentViewController(alertController,
25:         animated: true, completion: nil)
26: }


First, in line 2, we create scheduledAlert as an object of type UILocalNotification. This local notification object is what we set up with our desired message, sound, and so on and then pass off to the application to display sometime in the future.

In line 4, we use UIApplication.sharedApplication() to grab our application object and then call the UIApplication method cancelAllLocalNotifications. This cancels any previously scheduled notifications that this application may have made, giving us a clean slate.

In line 5, we configure the notification’s applicationIconBadgeNumber variable property so that when the notification is triggered, the application’s badge number is set to 1 to show that a notification has occurred.

Line 6 uses the fireDate along with the NSDate class method DateWithTimeIntervalSinceNow to set the notification to be triggered 300 seconds in the future.

Line 7 sets the timeZone for the notification. This should almost always be set to the local time zone, as returned by NSTimeZone.defaultTimeZone().

Line 8 sets the repeatInterval variable property for the notification. This can be chosen from a variety of constants, such as NSCalendarUnit.CalendarUnitDay (daily), NSCalendarUnit.CalendarUnitHour (hourly), and NSCalendarUnit.CalendarUnitMinute (every minute). You can find the full list in the NSCalendar class reference in the Xcode developer documentation.

In line 9, we set a sound to be played along with the notification. The soundName variable property is configured with a string with the name of a sound resource. Because we already have soundeffect.wav available in the project, we can use that without further additions.

Line 10 finishes the notification configuration by setting the alertBody of the notification to the message we want the user to see.

When the notification object is fully configured, we schedule it using the UIApplication method scheduleLocalNotification (lines 11–12). This finishes the implementation.

Choose Run to compile and start the application on your device or in the iOS Simulator. After GettingAttention is up and running, click the Alert Me! button. After the initial alert is displayed, click the Home button to exit the application. Go get a drink, and come back in about 4 minutes and 59 seconds. Exactly 5 minutes later, you’ll receive a local notification, as shown in Figure 22.5.

Image

FIGURE 22.5 Local notifications are displayed onscreen even when the application isn’t running.

Using Task-Specific Background Processing

So far, we haven’t actually done any real background processing. We’ve suspended an application and generated local notifications, but in each of these cases, the application hasn’t been doing any processing. Let’s change that. In our final two examples, we execute real code behind the scenes while the application is in the background. Although it is well beyond the scope of this book to generate a VoIP application, we can use our Cupertino application from the preceding hour’s lesson, with some minor modifications, to show background processing of location and audio.

Preparing the Cupertino Application for Audio

When we finished off the Cupertino application in the preceding hour, it told us how far away Cupertino was and presented straight, left, and right arrows on the screen to indicate the direction the user should be traveling to reach the mothership. We can update the application to audio using SystemSoundServices, just as we did in Hour 10’s GettingAttention application.

The only tricky thing about our changes is that we won’t want to hear a sound repeated if it was the same as the last sound we heard. To handle this requirement, we define a variable, lastSound, which is set to the last sound that has been played. We can then use this as a point of comparison to make sure that what we’re about to play isn’t the same thing we did just play.

Adding the AudioToolbox Framework

To use System Sound Services, we need to first add the AudioToolbox framework. Make a copy of the Cupertino compass application and name the folder Cupertino Audio Compass. Then open the Cupertino project in Xcode. Select the ViewController.swift file and add an import line for AudioToolbox after the existing import of the Core Location framework:

import AudioToolbox

Adding the Audio Files

Within the project folder included with this hour’s lesson, you’ll find an Audio folder containing simple direction sounds: straight.wav, right.wav, and left.wav. Drag the audio folder to the main project code group within the Xcode project. Choose to copy the files and create groups when prompted.

Adding System Sound ID Variables

Next, we need to update the code following the class line in ViewController.swift to declare four new variable properties for three SystemSoundIDs referencing our sound files (soundStraight, soundLeft, and soundRight) and a fourth (lastSound) to hold the last SystemSoundID we played. Remember that sound IDs aren’t objects, they’re just integers; so, we’ll initialize all four sound IDs to 0, giving us a clean starting point:

var soundStraight: SystemSoundID = 0
var soundRight: SystemSoundID = 0
var soundLeft: SystemSoundID = 0

var lastSound: SystemSoundID = 0

The setup is complete. We’re now ready to implement the code to generate the audio directions for the application.

Implementing the Cupertino Audio Directions

To add sound playback to the Cupertino application, we need to modify two of our existing ViewController methods. The viewDidLoad method will give us a good place to load all three of our sound files and set the soundStraight, soundRight, and soundLeft references appropriately.

Edit ViewController.swift and update the second half of viewDidLoad to match Listing 22.4.

LISTING 22.4 Initializing the Sound File References in viewDidLoad


 1: override func viewDidLoad() {
 2:     super.viewDidLoad()
 3:     locMan.delegate = self
 4:     locMan.desiredAccuracy =
 5:         kCLLocationAccuracyThreeKilometers
 6:     locMan.distanceFilter = 1609; // a mile
 7:     locMan.requestWhenInUseAuthorization()
 8:     locMan.startUpdatingLocation()
 9:
10:     if CLLocationManager.headingAvailable() {
11:         locMan.headingFilter = 10 // 10 degrees
12:         locMan.startUpdatingHeading()
13:     }
14:
15:     var soundFile:String!
16:
17:     soundFile = NSBundle.mainBundle()
18:         .pathForResource("straight", ofType: "wav")!
19:     AudioServicesCreateSystemSoundID(
20:         NSURL(fileURLWithPath: soundFile),&soundStraight)
21:
22:     soundFile = NSBundle.mainBundle()
23:         .pathForResource("right", ofType: "wav")!
24:     AudioServicesCreateSystemSoundID(
25:         NSURL(fileURLWithPath: soundFile),&soundRight)
26:
27:     soundFile = NSBundle.mainBundle()
28:         .pathForResource("left", ofType: "wav")!
29:     AudioServicesCreateSystemSoundID(
30:         NSURL(fileURLWithPath: soundFile),&soundLeft)
31: }



Tip

If you are having difficulties understanding the sound playback process, refer back to the Hour 10 tutorial.


The final logic that we need to implement is to play each sound when there is a heading update. The ViewController.swift method that implements this is locationManager:didUpdateHeading. Each time the arrow graphic is updated in this method, we prepare to play the corresponding sound with the AudioServicesPlaySystemSound function. Before we do that, however, we check to make sure that it isn’t the same sound as lastSound; this helps prevent a Max Headroom stuttering effect as one sound file is played repeatedly over top of itself. If lastSound doesn’t match the current sound, we play it and update lastSound with a new value. For the left arrow, for example, we might use this code fragment to play the sound and set the lastSound variable property:

if lastSound != soundLeft {
    AudioServicesPlaySystemSound(soundLeft)
    lastSound=soundLeft
}

Edit the locationManager:didUpdateHeading method as described. Your final result should look similar to Listing 22.5.

LISTING 22.5 Adding Audio Feedback When the Heading Updates


 1: func locationManager(manager: CLLocationManager,
 2:     didUpdateHeading newHeading: CLHeading) {
 3:     if (recentLocation != nil && newHeading.headingAccuracy >= 0) {
 4:         let cupertino:CLLocation =
 5:             CLLocation(latitude: kCupertinoLatitude,
 6:                 longitude: kCupertinoLongitude)
 7:         let course: Double =
 8:             headingToLocation(cupertino.coordinate,
 9:                 current:recentLocation.coordinate)
10:         let delta: Double = newHeading.trueHeading - course
11:         if (abs(delta) <= 10) {
12:             directionArrow.image =
13:                 UIImage(named: "up_arrow.png")
14:             if lastSound != soundStraight {
15:                 AudioServicesPlaySystemSound(soundStraight)
16:                 lastSound=soundStraight
17:             }
18:         } else {
19:             if (delta > 180) {
20:                 directionArrow.image =
21:                     UIImage(named: "right_arrow.png")
22:                 if lastSound != soundRight {
23:                     AudioServicesPlaySystemSound(soundRight)
24:                     lastSound=soundRight
25:                 }
26:             }
27:             else if (delta > 0) {
28:                 directionArrow.image =
29:                     UIImage(named: "left_arrow.png")
30:                 if lastSound != soundLeft {
31:                     AudioServicesPlaySystemSound(soundLeft)
32:                     lastSound=soundLeft
33:                 }
34:             }
35:             else if (delta > -180) {
36:                 directionArrow.image =
37:                     UIImage(named: "right_arrow.png")
38:                 if lastSound != soundRight {
39:                     AudioServicesPlaySystemSound(soundRight)
40:                     lastSound=soundRight
41:                 }
42:             }
43:             else {
44:                 directionArrow.image =
45:                     UIImage(named: "left_arrow.png")
46:                 if lastSound != soundLeft {
47:                     AudioServicesPlaySystemSound(soundLeft)
48:                     lastSound=soundLeft
49:                 }
50:             }
51:         }
52:         directionArrow.hidden = false
53:     } else {
54:         directionArrow.hidden = true
55:     }
56: }


The application is now ready for testing. Click Run to install the updated Cupertino application on your device, and then try moving around. As you move, it will speak “right,” “left,” and “straight” to correspond to the onscreen arrows. Try exiting the applications and see what happens. Surprise. It won’t work. That’s because we haven’t yet updated the project to allow background processing.


Tip

If you’re testing the application and it still seems a bit “chatty” (playing the sounds too often), you might want to update locMan.headingFilter to a larger value (like 15 or 20) in the viewDidLoad method. This will help cut down on the number of heading updates.


Adding the Background Modes

Our application performs two tasks that should remain active when in a background state. First, it tracks our location. Second, it plays audio to give us a general heading. We need to add both audio and location background mode capabilities to the application for it to work properly. Update the Cupertino project by following these steps:

1. Choose the main project group and click the Cupertino target, and then expand the Background Modes section under the Capabilities tab.

2. Click the switch to turn on Background Modes.

3. Check both the “Audio, Airplay and Picture in Picture” and the “Location Updates” check boxes, as shown in Figure 22.6.

Image

FIGURE 22.6 Add the background modes required by your application.

After updating the capabilities, install the updated application on your device and try again. This time, when you exit the application, it will continue to run. As you move around, you’ll hear spoken directions as Cupertino continues to track your position behind the scenes.

By declaring the location and audio background modes, your application is able to use the full services of Location Manager and iOS’s many audio playback mechanisms when it is in the background.


Tip: iPad Picture in Picture

Notice that when you add the mode for playing audio in the background, you’re also adding Picture in Picture capabilities? Yes, this actually does work. To see what I mean, go back to the MediaPlayground project in Hour 19, “Working with Rich Media,” and turn on the same background mode. Start the project, and then load and play the video. As it is playing, press the home button on your device. The application exits, and the video detaches into a small window on your iPad that continues to play!

For more information about interacting with Picture in Picture features, read the Picture in Picture Quick Start found in the Xcode documentation.


Completing a Long-Running Background Task

In our next tutorial of the hour, we need to create a project from scratch. Our book isn’t about building applications that require a great deal of background processing, so we need to be creative to demonstrate this feature. Sure, we could add code to an existing project that would allow a method to run in the background, but we don’t have any long-running methods that could make use of it.

Implementation Overview

To demonstrate how we can tell iOS to allow something to run in the background, we create a new application, SlowCount, that does nothing but count to 1,000—slowly. We use the task-completion method of background to make sure that, even when the application is in the background, it continues to count until it reaches 1,000 (as shown in Figure 22.7).

Image

FIGURE 22.7 To simulate a long-running task, our application will count slowly.

Setting Up the Project

Create a new single-view application named SlowCount. We move through development fairly quickly because, as you can imagine, this application is pretty simple.

Planning the Variables and Connections

The application has a single outlet, a UILabel named theCount, which we use to present the counter onscreen. In addition, it needs several variable properties: an integer to use as a counter (count), an NSTimer object that triggers the counting at a steady interval (theTimer), and a UIBackgroundTaskIdentifier variable (not an object) that we use to reference the task we have running in the background (counterTask).


Note

Every task that you want to enable for background task completion needs its own UIBackgroundTaskIdentifier. This is used along with the UIApplication method endBackgroundTask to identify which background task has just ended.


Designing the Interface

It’s a bit of a stretch to claim that this application has a user interface (UI), but we still need to prepare Main.storyboard to show the theCount label on the screen.

Open the initial scene, and drag a label (UILabel) into the center of the view. Set the label’s text to read 0. With the label selected, use the Attributes Inspector (Option-Command-4) to set the label alignment to center and the font size to something a bit bigger. Finally, align the right and left sides of the label with the right and left sizing guides. You’ve just created a UI masterpiece, as shown in Figure 22.8.

Image

FIGURE 22.8 Add a UILabel to the view to hold the current count.

Creating and Connecting the Outlet

We’ve got one UI object to connect to a single outlet. Switch to the assistant editor, and then Control-drag from the label to below the class line in ViewController.swift. Name the outlet theCount when prompted to make the connection.

Implementing the Application Logic

To finish our application’s core functionality (counting), we need to declare and deal with the additional variable properties: the counter (count), the NSTimer object to give us a nice delay while counting (theTimer), and a UIBackgroundTaskIdentifier to track the task (counterTask). In addition, we implement a method that does the counting (and nothing else) called countUp.

Update the ViewController.swift file after the existing @IBOutlet to declare the variable properties:

var count: Int = 0
var counterTask: UIBackgroundTaskIdentifier!
var theTimer: NSTimer!

With most of the prep work done, we have two more things left to complete. First, we need to initialize the NSTimer to fire at a regular interval. Second, when the timer fires, we ask it to invoke a second method, countUp. In the countUp method, we check to see whether count is 1000. If it is, we turn off the timer and we’re done; if not, we update count and display it in our UILabel theCount.

Initializing the Timer and Counter

Let’s start with initializing the timer. What better place to do this than in the viewDidLoad method? Implement viewDidLoad, as shown in Listing 22.6.

LISTING 22.6 Scheduling a Timer When the Application Starts


1: override func viewDidLoad() {
2:     super.viewDidLoad()
3:
4:     theTimer = NSTimer.scheduledTimerWithTimeInterval(0.1, target: self,
5:         selector: "countUp", userInfo: nil, repeats: true)
6: }


Lines 4–5 initialize the theTimer NSTimer object with an interval of 0.1 seconds. The selector is set to use the method countUp, which we write next. The timer is set to keep repeating with repeats:true.

All that remains is to implement countUp so that it increments the counter and displays the result.

Updating the Counter and Display

Add the countUp method, as shown in Listing 22.7, in ViewController.swift. This should be quite straightforward: If the count equals 1000, we’re done and it’s time to clean up (we can disable the timer); otherwise, we count.

LISTING 22.7 Updating the Counter


1: func countUp() {
2:     if count==1000 {
3:         theTimer.invalidate()
4:     } else {
5:         count++
6:         theCount.text="(count)"
7:     }
8: }


Lines 2–4 handle the case where we’ve reached the limit of our counting (count==1000). When that occurs, we use the timer’s invalidate method to stop it because it isn’t needed anymore (line 3).

Lines 4–7 handle the actual counting and display. Line 5 updates the count variable property. Line 6 updates our theCount label with the contents of count.

Run the application. It should do exactly what you expect: count slowly until it reaches 1,000. Unfortunately, if you background the application, it will suspend. The counting will cease until the application returns to the foreground.

Enabling the Background Task Processing

To enable the counter to run in the background, we need to mark it as a background task. We use this code snippet to mark the beginning of the code we want to execute in the background:

counterTask =
    UIApplication.sharedApplication()
    .beginBackgroundTaskWithExpirationHandler({ () -> Void in
     // If you're worried about exceeding 10 minutes, handle it here
    })

And we use this code snippet to mark the end:

UIApplication.sharedApplication().endBackgroundTask(counterTask)


Note

If you are worried about the application not finishing the background task before it is forced to end (roughly 10 minutes), you could implement the optional code in the beginBackgroundTaskWithExpirationHandler closure. You can always check to see how much time is remaining by checking the UIApplication variable property backgroundTimeRemaining.


Let’s update our viewDidLoad and countUp methods to include these code additions.

In viewDidLoad, we start the background task right before we initialize the counter. In countUp, we end the background task after count==1000 and the timer is invalidated.

Update viewDidLoad, as shown in Listing 22.8 (lines 4–8).

LISTING 22.8 Setting the Start of Background Processing


 1: override func viewDidLoad() {
 2:     super.viewDidLoad()
 3:
 4:     counterTask =
 5:         UIApplication.sharedApplication()
 6:             .beginBackgroundTaskWithExpirationHandler({ () -> Void in
 7:             // If you're worried about exceeding 10 min, handle it here
 8:         })
 9:
10:     theTimer = NSTimer.scheduledTimerWithTimeInterval(0.1, target: self,
11:         selector: "countUp", userInfo: nil, repeats: true)
12: }


Then make the corresponding additions to countUp (line 4), as demonstrated in Listing 22.9.

LISTING 22.9 Setting the End of Background Processing


1: func countUp() {
2:     if count==1000 {
3:         theTimer.invalidate()
4:         UIApplication.sharedApplication().endBackgroundTask(counterTask)
5:     } else {
6:         count++
7:         theCount.text="(count)"
8:     }
9: }


That’s all it takes. Your project should now be able to run in the background.


For This to Work, Don’t I Need to Enable Background Modes?

No. Background modes are used only for task-specific background multitasking. The code for handling long-running tasks doesn’t require any special capabilities to be defined.


Building the Application

Run the application on your device or in the Simulator. After the counter starts counting, press the Home button to move the application to the background. Wait a minute or so, and then reopen the application through the task manager or the application icon. The counter will have continued to run in the background.

Obviously, this isn’t a very compelling project itself, but the implications for what can be achieved in real-world apps is definitely exciting.

Performing a Background Fetch

Our next tutorial is, in your author’s opinion, one of the most interesting: implementing a background fetch. If you recall from the introduction, this is the ability of an application to periodically activate and retrieve information without the user needing to do a thing. We’ll make this happen in the final project (and with just a surprisingly small amount of code).

Implementation Overview

The purpose of our new application, BackgroundDownload, is to download and display a new background image using a background fetch. Depending on how often a user uses the application, he’ll see a new image each time it starts (without having to wait for it to download). There isn’t a UI to speak of, beyond the UIImageView used as the background, as shown in Figure 22.9.

Image

FIGURE 22.9 The background fetch will periodically download a new image, even when the application isn’t running.

Setting Up the Project

As always, create a new single-view application named BackgroundDownload. This application is even simpler than the last, so we’ll be finished before you know it.

And we’re done.

Not really.

Planning the Variables and Connections

BackgroundDownload will have a single outlet, a UIImageView named backgroundImage, which will contain the background image.

Designing the Interface

Open the Main.Storyboard file and drag a UIImageView into the initial scene’s view. Size it to cover the entire view, and then open the Attributes Inspector and set the image view’s View mode to Aspect Fill, as shown in Figure 22.10.

Image

FIGURE 22.10 Configure the image view to be Aspect Fill.

Creating and Connecting the Outlet

Like the last project, there’s a single UI element (the UIImageView) that we need to connect to an outlet (backgroundImage). Switch to the assistant editor, and be sure to select the ViewController.swift file on the right.

Control-drag from the UIImageView to below the class line in ViewController.swift. Name the outlet backgroundImage when prompted to make the connection.

Implementing the Application Logic

To add the background fetching functionality, we will be making some edits in the AppDelegate class. First, we’ll define the minimum frequency with which the application requests background fetches. This consists of a single line within application:didFinishLaunchingWithOptions. Open AppDelegate.swift and edit the method, as shown in Listing 22.10.

LISTING 22.10 Defining the Minimum Background Fetch Interval


func application(application: UIApplication, didFinishLaunchingWithOptions
    launchOptions: [NSObject: AnyObject]?) -> Bool {
    // Override point for customization after application launch.
    UIApplication.sharedApplication().setMinimumBackgroundFetchInterval(
        UIApplicationBackgroundFetchIntervalMinimum)
    return true
}


The method setMinimumBackgroundFetchInterval defines the minimum amount of time between fetches. Rather than setting an exact time, we use the constant UIApplicationBackgroundFetchIntervalMinimum to tell iOS that we want the fetch to occur as often as possible.

Now that the application knows we want to perform background updates, we must implement the method application:performFetchWithCompletionHandler. The method will retrieve an image from a website, then use it to set the image variable property of the backgroundImage ViewController variable property.

Implement the application:performFetchWithCompletionHandler using Listing 22.11 as your guide. We’ll go through the details afterward.

LISTING 22.11 Finishing the Background Fetch by Implementing application:performFetchWithCompletionHandler


 1: func application(application: UIApplication,
 2:     performFetchWithCompletionHandler completionHandler:
 3:     (UIBackgroundFetchResult) -> Void) {
 4:
 5:     let url: NSURL = NSURL(string:
 6:         "https://teachyourselfios.info/?hour=22")!
 7:     let data: NSData = NSData(contentsOfURL: url)!
 8:     let imageData: UIImage = UIImage(data: data)!
 9:     let myViewController: ViewController =
10:         self.window!.rootViewController! as! ViewController
11:     myViewController.backgroundImage.image = imageData
12:
13:     //Indicate completion
14:     completionHandler(UIBackgroundFetchResult.NewData)
15: }


Lines 5–6 allocates an NSURL object with a web address that will return a random image from teachyourselfios.info. Line 7 reads the data returned by the URL into an NSData object named data. At this point, we’ve performed the background fetch; now we just need to do something with the image we’ve downloaded.

In Line 8, that data is transformed into a UIImage using the UIImage convenience method imageWithData. In lines 9–10, we take advantage of the fact that the window object defined in the application delegate has a reference to the active view controller through the variable property rootViewController. This is cast as an instance of our ViewController class and stored in myViewController so that it’s easier to access.

Line 11 accesses the backgroundImage variable property of ViewController and sets image to the image we downloaded.

Line 14 is a required function call to completionHandler tell iOS that we’ve successfully completed a background fetch operation. Depending on the outcome, we might also pass the constant UIBackgroundFetchResult.Failed (to indicate a failure) or UIBackgroundFetchResult.NoData (if nothing has changed).

Adding the Background Fetch Mode

Only one thing remains before we can give this a try: setting the Background Fetch background mode. Update the BackgroundDownload project by completing these steps:

1. Choose the main project group and click the BackgroundDownload target, and then expand the Background Modes section under the Capabilities tab.

2. Click the switch to turn on Background Modes.

3. Check the Background Fetch check box, as shown in Figure 22.11.

Image

FIGURE 22.11 Add the Background Fetch mode to your application.

Building the Application

Go ahead and run the finished BackgroundDownload application in the iOS Simulator. You’ll notice that it does absolutely nothing! The reason you won’t see a background image is because we’ve only implemented fetching of an image when the application is in the background. You can either move the app to the background and wait, or you can choose Debug, Simulate Background Fetch from the Debug menu in Xcode. This automatically exits the app and forces it to perform a fetch. The next time you start it, the background image will be set.

Adding 3D Touch Quick Actions

In the final exercise of the hour, we’ll be updating the ImageHop application (yes, again) to support 3D Touch Quick Actions. Quick Actions enables your users (assuming they have a 3D Touch-capable device) to access common application functions directly from the application icon—before the app even launches.

In the case of ImageHop, we’ll add the option of having the user launch the application to a slow hopping bunny, or a fast hopping bunny (without them needing to configure a speed or touch the Hop! button). This requires surprisingly little code, but we will need a few more resources added to the application.

Adding the Quick Action Icons

Quick Actions often have an icon associated with them to give the user a more visual experience. I’ve included two new icons (slow and fast) in the folder BunnyIcons included in this hour’s project folder. Let’s load them now.

Begin by creating a new copy of the ImageHop application. Use the original project as the starting point, rather than the modified version that we created earlier this hour. Open the project and use the project navigator to select the Assets.xcassets asset catalog. Drag the BunnyIcons folder into the left column of the catalog—below the existing AppIcon and Images entries.

Once the icons are added, we can reference them as slow and fast within our Quick Action configuration.

Adding the Quick Action Definitions

Unlike many features in iOS, defining Quick Actions does not require any coding. We will, however, need to edit the Info.plist file for the project. Select it now so that the contents appear in the Xcode editor.

The first step is to add an array with the key UIApplicationShortcutItems to the Information property list:

1. Right-click anywhere in the existing list and choose Add Row.

2. When the row appears, type UIApplicationShortcutItems as the key.

3. Click the Type column to the right of the key and choose Array.

For each Quick Action you want to add, you must add a dictionary (with several keys and values) to the shortcut items array. We have two Quick Actions we’re adding in this exercise. We’ll start with the Slow Hop action:

1. Make sure that the disclosure arrow is expanded (pointing down) in front of the UIApplicationShortcutItems entry in the plist.

2. With your cursor hovering over the UIApplicationShortcutItems keyword, you’ll see a + and – button appear to the right. Click +.

3. A new item will appear within the array. Click the Type column for the item and change it to Dictionary.

4. Much as you did for the parent Array, expand the disclosure arrow for Dictionary item.

5. Click the + button beside the Dictionary item three times to add three new entries within the dictionary. These will appear as String items.

6. Set the Key of one of the new String items to UIApplicationShortcutItemTitle with a value of. This is the Quick Action title displayed to the user.

7. Set the Key of another String item to UIApplicationShortcutItemType and a value of startSlowHop. This is the value our application will receive when the Quick Action is executed.

8. Set the third String item to a Key of UIApplicationShortcutItemIconFile and a value of slow. This is the name of the icon image to be displayed in the Quick Action.

9. You’ve just configured a 3D Touch Quick Action! Our application has two actions, so repeat steps 2–8, but supplying the values of Start Fast Hop, startFastHop, and fast.

Figure 22.12 shows the completed 3D Touch Quick Action configuration.

Image

FIGURE 22.12 Configure your Quick Actions within the project’s Info.plist file.

Implementing the Quick Action Logic

If you test the application on a 3D Touch-enabled device, the Quick Actions should appear when you press firmly on the application’s icon. Unfortunately, at the time of this writing, there is no way to test this in the iOS Simulator.

Regardless, even the actions, while present, don’t actually do anything yet. We still need to implement the method application:performActionForShortcutItem to process the chosen action. Open the AppDelegate.swift file and add Listing 22.12 to the file.

LISTING 22.12 Reacting to Quick Action events


 1:  func application(application: UIApplication,
 2:    performActionForShortcutItem shortcutItem:UIApplicationShortcutItem,
 3:      completionHandler: (Bool) -> Void) {
 4:
 5:      let myViewController: ViewController =
 6:          self.window!.rootViewController! as! ViewController
 7:
 8:      var success: Bool = false
 9:
10:      if shortcutItem.type == "startSlowHop" {
11:          myViewController.speedSlider.value = 0.25
12:          myViewController.setSpeed(nil)
13:          success = true
14:      } else if shortcutItem.type == "startFastHop" {
15:          myViewController.speedSlider.value = 1.75
16:          myViewController.setSpeed(nil)
17:          success = true
18:      } else {
19:          success = false
20:      }
21:
22:      completionHandler(success)
23:  }


To start the bunnies hopping, we can use the setSpeed method, which looks at the value of the speedSlider and starts the bunnies moving. To make things go slow and fast, we’ll set the speedSlider.value as if the user had adjusted the speed himself. Before we can do any of that, however, we need to be able to access setSpeed and speedSlider, which are defined in the ViewController.swift class. Lines 5–6 grab a reference to ViewController object, and store it in myViewController for easy access.

Line 8 defines a Boolean value, success, that will be used to track whether we have been successful (or failed) in processing the Quick Actions.

Lines 10, 14, and 18 examine shortcutItem.type to see which Quick Action was executed. If shortcutItem.type is equal to startSlowHop (line 10), the user picked the Start Slow Hop action. If it is equal to startFastHop (line 14), the Start Fast Hop action was selected. If neither is true (line 18), something has gone wrong.

Lines 11–12 and 15–16 set the speed and start the bunnies hopping at the desired rate.

Line 22 finishes up by calling the completionHandler method with true or false, depending on whether we could properly react to the Quick Action. This, incidentally, is required by iOS, not something I added just to take up space.

Building the Application

You can now launch the application on a 3D Touch-enabled iOS device (currently the iPhone 6s/6s+). When you firmly press on the application icon, you’ll see the Quick Action menu appear, as shown in Figure 22.13.

Image

FIGURE 22.13 A firm press reveals the 3D Touch Quick Actions.

Choosing an action immediately launches ImageHop (or moves it to the foreground), configures the speed slider to an appropriate value, and then starts the bunnies happily hopping.

Further Exploration

When I sat down to write this lesson, I was torn. Background tasks/multitasking is definitely the “must have” feature of iOS, but it’s a challenge to demonstrate anything meaningful in the span of a dozen or two pages. What I hope we’ve achieved is a better understanding of how iOS multitasking works and how you might implement it in your own applications. Keep in mind that this is not a comprehensive guide to background processing; there are many more features available and many ways that you can optimize your background-enabled apps to maximize battery life and speed.

As a next step, you should read the sections “App States and Multitasking” and “Background Execution and Multitasking” in Apple’s iOS Application Programming Guide (available through the Xcode documentation).

As you review Apple’s documentation, pay close attention to the tasks that your application should be completing as it works in the background. There are implications for games and graphic-intensive applications that are well beyond the scope of what we can discuss here. How well you adhere to these guidelines will determine whether Apple accepts your application or kicks it back to you for optimization.

Summary

Background applications on iOS devices are not the same as background applications on your Macintosh. There are well-defined rules that background-enabled applications must follow to be considered “good citizens” of iOS. In this hour’s lesson, you learned about the different types of backgrounding available and the methods available to support background tasks. Over the course of seven tutorial applications, you put these techniques to the test, creating everything from notifications triggered when an application isn’t running, to a navigation app with background voice prompting, an application that automatically updates its content when it isn’t actively running, and Quick Actions that display outside of an application’s main interface.

You should now be well prepared to create your own background-capable apps and take full advantage of the powerful hardware in your iPhone, iPad, or iPod.

Q&A

Q. Why can’t I run any code I want in the background?

A. Someday I suspect you will, but for now the platform is constrained to the specific types of background processing we discussed. The security and performance implications of running anything and everything on a device that is always connected to the Internet are enormous. Apple intends to ensure that your device remains operational in any conditions, unlike the competitors, where anything goes.

Q. If my application handles background operations, do I need to worry about iOS forcing my app to quit?

A. Absolutely. The currently executing application (in the foreground) always has priority. Your application should be prepared to exit at any time if resources are constrained.

Workshop

Quiz

1. To start a long running task, which of the following methods would you use?

a. backgroundTaskWithExpirationHandler

b. startBackgroundTaskWithExpirationHandler

c. beginBackgroundTaskWithExpirationHandler

d. begin]TaskWithExpirationHandler

2. To clear any pending local notifications for your app, you should use which method?

a. cancelNotifications

b. disableAllLocalNotifications

c. removeAllLocalNotifications

d. cancelAllLocalNotifications

3. Playing audio is an example of what type of backgrounding?

a. Task-specific

b. Forbidden

c. Automatic

d. Time-limited

4. Before scheduling a local notification, you must first ask for what?

a. Memory

b. Authorization

c. Network resources

d. Data storage

5. A background fetch has how many seconds to complete?

a. 30

b. 10

c. 60

d. 120

6. Which method enables you to stop an NSTimer when you are finished with it?

a. close

b. finish

c. delete

d. invalidate

7. To enable background processing of GPS data, you use which capability check box?

a. Location Services

b. Location Updates

c. GPS Updates

d. User Data Updates

8. A 3D Touch Quick Action’s title is defined by which plist key?

a. UIApplicationQuickItemTitle

b. UIApplicationQuickActionTitle

c. UIApplicationShortcutItemTitle

d. UIApplicationLaunchItemTitle

9. To request the frequency of background fetches, you must use which method?

a. setMinimumBackgroundFetchInterval

b. setMaximumBackgroundFetchInterval

c. setMinimumInterval

d. setMaximumInterval

10. To set the message displayed in a local notification, you can use which variable property?

a. alertText

b. alertInfo

c. alertBody

d. alertDisplay

Answers

1. C. Long-running tasks are started by including a call to the beginBackgroundTaskWithExpirationHandler method.

2. D. Use the cancelAllLocalNotifications to clear any existing notifications that your application may have generated.

3. A. Background audio is an example of task-specific backgrounding.

4. B. You must request authorization from the user before attempting to use local notifications.

5. A. Background fetches must complete within 30 seconds; otherwise, iOS will terminate them automatically.

6. D. The invalidate method is used to stop an NSTimer when you are finished using it.

7. B. The Location Updates check box must be checked in order for your application to receive and process changes in the user’s location while in the background.

8. C. Quick Action titles are configured using the property list key of UIApplicationShortcutItemTitle.

9. A. You can only set the minimum amount of time between background fetches; this is managed through the setMinimumBackgroundFetchInterval variable property.

10. C. The alertBody is used to set the message contents for a local notification.

Activities

1. Return to a project in an earlier hour and properly enable it for background processing or 3D Touch Quick Actions.

2. Test to see what happens when an application with background processing attempts to run longer than 10 minutes. Is it suspended? Terminated? How do you recover from this if your application does not finish its task?

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.14.235