Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Mobile / iPhone

Hour 21: Building Background-Aware Applications

4.87/5 (10 votes)
2 Nov 2010CPOL28 min read 81.7K  
In this chapter excerpt from Sams Teach Yourself iPhone Application Development in 24 Hours we'll look at: How iOS 4 supports background tasks, what types of background tasks are supported, how to disable backgrounding, how to suspend applications, and how to execute code in the background.

ShowCover.jpg

John Ray
Published by Sams
ISBN-10: 0-672-33220-5
ISBN-13: 978-0-672-33220-3

What You'll Learn in This Hour:

  • How iOS 4 supports background tasks
  • What types of background tasks are supported
  • How to disable backgrounding
  • How to suspend applications
  • How to execute code in the background

"The ability to run multiple applications in the background" mocks the Verizon commercial. "Why can't a modern operating system run multiple programs at once?" question the discussion groups. As a developer and a fan of the iPhone, I've found these threads amusing in their naiveté and somewhat confusing. The iPhone has 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 as a phone. Rather than an "anything goes" approach, Apple has taken steps to ensure that the phone remains responsive at all times.

With the release of iOS 4.x, Apple answers the call from the competition by opening up background processing to third-party applications. Unlike the competitors, however, Apple has been cautious in how it approached backgrounding—opening it up to a specific set of tasks that users commonly encounter. In this hour's lesson, you learn several of the multitasking techniques that you can implement in iOS 4.

Understanding iOS 4 Backgrounding

If you've been working in iOS 4.x or later as you've built the tutorials in this book, you may have noticed that when you quit the applications on your phone or in the iPhone 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 created in iOS 4.x are background-ready as soon as you click the Build and Run button. That doesn't mean that they will run in the background, just that they're aware of the new iOS 4 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, then the application life cycle methods.

Types of Backgrounding

We explore four primary types of backgrounding in iOS 4.x: application suspension, local notifications, task-specific background processing, and task completion.

Suspension

When an application is suspended, it will cease executing code but be 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 will be stopped, keeping the app from using up your iPhone's resources. Any application that you compile against iOS 4.x 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" section, later in this chapter), but beyond that, it "just works."

In addition to performing cleanup as an application is being suspended, it will be 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 to appear onscreen at a point in time in the future. For example, the following code initializes a notification (UILocationNotification) configures it to appear in five minutes, and then uses the application's scheduleLocalNotification method to complete the scheduling:

UILocalNotification *futureAlert;
futureAlert = [[[UILocalNotification alloc] init] autorelease];
futureAlert.fireDate = [NSDatedateWithTimeIntervalSinceNow:300];
futureAlert.timeZone = [NSTimeZonedefaultTimeZone];
[[UIApplication sharedApplication] scheduleLocalNotification:futureAlert];

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 View button in the notification window to return to your application.

Task-Specific Background Processing

Before Apple decided to implement background processing, they did some research on how users worked with their handhelds. What they found was that there were specific types of background processing that people needed. 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, 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 4.x. 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 values of App Plays Audio (Audio), App Registers for Location Updates (Location), or App Provides Voice over IP Services (VoIP).

Task Completion for Long-Running Tasks

The fourth type of backgrounding that we'll be using in iOS 4.x is task completion. Using task-completion methods, you can "mark" the tasks in your application that will need to finish before it 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:

UIBackgroundTaskIdentifier myLongTask;

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 = [[UIApplicationsharedApplication]
              beginBackgroundTaskWithExpirationHandler:^{
                 // 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, plenty of time for most common uses. After the time completes, the application is suspended and treated like any other suspended application.

The Background-Aware Application Life Cycle Methods

In Hour 4, "Inside Cocoa Touch," you started learning about the application life cycle, as shown in Figure 21.1. You learned that, in iOS 4.x, 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.

21fig01.jpg

Figure 21.1 The iOS 4.x application life cycle.

In addition to applicationDidEnterBackground, there are several other methods that you should implement to be a proper background-aware iOS citizen. For many small applications, you won't need to do anything with these, other than leave them as is in the application delegate. As your projects increase in complexity, however, you'll want to 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 - 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.

The methods that Apple expects to see in your background-aware apps are as follows:

  • application:didFinishLaunchingWithOptions: Called when your application first launches. If your application is terminated while suspended, or purged from memory, needs to restore its previous state manually. (You did save it your user's preferences, right?)
  • 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.
  • 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.
  • applicationDidEnterBackground: Called when the application has become a background application. This replaces applicationWillTerminate in iOS 4.x. 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.
  • applicationWillEnterForeground: Called when an application returns to an active state after being backgrounded.
  • 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 iOS 4.x application delegate implementation files. 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.

Caution - The assumption in this hour's lesson is that you are using iOS 4.x or later. If you are not, using background-related methods and properties on earlier versions of the OS will result in errors. To successfully target both iOS 4.x and earlier devices, check to see whether backgrounding is available, and then react accordingly in your apps.

Apple provides the following code snippet in the iPhone Application Programming Guide for checking to see (regardless of OS version) whether multitasking support is available:

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

If the resulting backgroundSupported Boolean is YES, you're safe to use background-specific code.

Now that you have an understanding of the background-related methods and types of background processing available to you, let's look at how they can be implemented. To do this, we'll reuse tutorials that we've built throughout the book (with one exception). We won't be covering 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, there are many different "diversion" apps that 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 started 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. Open the project's plist file in the resources group (HelloNoun-Info.plist).
  3. Add an additional key to the property list, selecting Application Does Not Run in Background (UIApplicationExitsOnSuspend) from the Key pop-up menu.
  4. Click the check box beside the key, as shown in Figure 21.2.
  5. Save the changes to the plist file.

21fig02.jpg

Figure 21.2 Add the Application Does Not run in Background (UIApplication-ExitsOnSuspend) key to the project.

Build and run the application on your iPhone or in the iPhone simulator. When you exit the application with the Home button, it will not be suspended, nor will not show in the task manager, 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 4.x 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, and Sliders." 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'll edit the application delegate method applicationWillEnterForeground. Recall that this method is invoked only when an application is returning from a backgrounded state. Open ImageHopAppDelegate.m and implement the method, as shown in Listing 21.1.

Listing 21.1

- (void)applicationWillEnterForeground:(UIApplication *)application {
    UIAlertView *alertDialog;
    alertDialog = [[UIAlertView alloc]
                   initWithTitle: @"Yawn!"
                   message:@"Was I asleep?"
                   delegate: nil
                   cancelButtonTitle: @"Welcome Back"
                   otherButtonTitles: nil];
    [alertDialog show];
    [alertDialog release];
}

Within the method, we declare, initialize, show, and release an alert view, exactly as we did in the "Getting Attention" tutorial in Hour 10, "Getting the User's Attention." After updating the code, build and 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 Build and 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 21.3.

21fig03.jpg

Figure 21.3 The application WillEnterFore-ground 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'll need beyond those few lines! To demonstrate the use of local notifications, we'll be updating Hour 10's "Getting the User's Attention" doAlert method. Instead of just displaying an alert, it will also show a notification 5 minutes later and then schedule local notifications to occur every day thereafter.

Common Notification Properties

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

  • applicationIconBadgeNumber: An integer that is displayed on the application icon when the notification is triggered
  • fireDate: An NSDate object that provides a time in the future for the notification to be triggered
  • timeZone: The time zone to use for scheduling the notification
  • repeatInterval: How frequently, if ever, the notification should be repeated
  • soundName: A string (NSString) containing the name of a sound resource to play when the notification is triggered
  • 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 21.2. (Bolded lines are additions to the existing method.) Once the code is in place, we'll walk through it together.

Listing 21.2

 1: -(IBAction)doAlert:(id)sender {
 2:     UIAlertView *alertDialog;
 3:     UILocalNotification *scheduledAlert;
 4:
 5:     alertDialog = [[UIAlertView alloc]
 6:                    initWithTitle: @"Alert Button Selected"
 7:                  message:@"I need your attention NOW (and in alittle bit)!"
 8:                    delegate: nil
 9:                    cancelButtonTitle: @"Ok"
10:                    otherButtonTitles: nil];
11:
12:     [alertDialog show];
13:     [alertDialog release];
14:
15:
16:     [[UIApplication sharedApplication] cancelAllLocalNotifications];
17:     scheduledAlert = [[[UILocalNotification alloc] init] autorelease];
18:     scheduledAlert.applicationIconBadgeNumber=1;
19:     scheduledAlert.fireDate = [NSDate dateWithTimeIntervalSinceNow:300];
20:     scheduledAlert.timeZone = [NSTimeZone defaultTimeZone];
21:     scheduledAlert.repeatInterval =  NSDayCalendarUnit;
22:     scheduledAlert.soundName=@"soundeffect.wav";
23:     scheduledAlert.alertBody = @"I'd like to get your attention again!";
24:
25:     [[UIApplication sharedApplication]        ÂscheduleLocalNotification:scheduledAlert];
26:
27: }

First, in line 3, we declare 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 16, 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.

Line 17 allocates and initializes the local notification object scheduledAlert. Because the notification is going to be handled by iOS rather than our GettingAttention application, we can use autorelease to release it.

In line 18, we configure the notification's applicationIconBadgeNumber 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 19 uses the fireDate property along with the NSDate class method dateWithTimeIntervalSinceNow to set the notification to be triggered 300 seconds in the future.

Line 20 sets the timeZone for the notification. This should almost always be set to the local time zone, as returned by [NSTimeZone defaultTimeZone].

Line 21 sets the repeatInterval property for the notification. This can be chosen from a variety of constants, such as NSDayCalendarUnit (daily), NSHourCalendarUnit (hourly), and NSMinuteCalendarUnit (every minute). The full list can be found in the NSCalendar class reference in the Xcode developer documentation.

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

Line 23 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 (line 25). This finishes the implementation!

Choose Build and Run to compile and start the application on your iPhone or in the iPhone 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. At exactly 5 minutes later, you'll receive a local notification, as shown in Figure 21.4.

21fig04.jpg

Figure 21.4 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'll 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 last 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 last 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'll use a constant for each sound: 1 for straight, 2 for right, and 3 for left, and store this in a variable called lastSound each time a sound is 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. Open the Cupertino (with Compass implementation) project in Xcode. Right-click the Frameworks group and choose Add, Existing Frameworks. Choose AudioToolbox.framework from the list that appears, and then click Add, as shown in Figure 21.5.

Adding the Audio Files

Within the Cupertino Audio Compass - Navigation and Audio folder included with this hour's lesson, you'll find an Audio folder. Drag the files from the audio folder (straight.wav, right.wav, and left.wav) to the Resources group within the Xcode project. Choose to copy the files into the application when prompted, as shown in Figure 21.6.

21fig05.jpg

Figure 21.5 Add the Audio- Toolbox.framework to the project.

21fig06.jpg

Figure 21.6 Add the necessary sound resources to the project.

Updating the CupertinoViewController.h Interface File

Now that the necessary files are added to the project, we need to update the CupertinoViewController interface file. Add an #import directive to import the AudioToolbox interface file, and then declare instance variables for three SystemSoundIDs (soundStraight, soundLeft, and soundRight) and an integer lastSound to hold the last sound we played. Remember that these aren't objects, so there's no need to declare the variables as pointers to objects, add properties for them, or release them!

The updated CupertinoViewController.h file should resemble Listing 21.3.

Listing 21.3

#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#import <AudioToolbox/AudioToolbox.h>
 
@interface CupertinoViewController : UIViewController
<CLLocationManagerDelegate> {
 
    CLLocationManager *locMan;
    CLLocation *recentLocation;
    IBOutlet UILabel *distanceLabel;
    IBOutlet UIView  *distanceView;
    IBOutlet UIView  *waitView;
    IBOutlet UIImageView *directionArrow;
    SystemSoundID soundStraight;
    SystemSoundID soundRight;
    SystemSoundID soundLeft;
    int lastSound;
 
}
 
@property (assign, nonatomic) CLLocationManager *locMan;
@property (retain, nonatomic) CLLocation *recentLocation;
@property (retain, nonatomic) UILabel *distanceLabel;
@property (retain, nonatomic) UIView *distanceView;
@property (retain, nonatomic) UIView *waitView;
@property (retain, nonatomic) UIView *directionArrow;
 
-(double)headingToLocation:(CLLocationCoordinate2D)desired
                   current:(CLLocationCoordinate2D)current;
 
@end

Adding Sound Constants

To help keep track of which sound we last played, we declared the lastSound instance variable. Our intention is to use this to hold an integer representing each of our three possible sounds. Rather than remembering that 2 = right, and 3 = left, and so on, let's add some constants to the CupertinoViewController.m implementation file to keep these straight.

Insert these three lines following the existing constants we defined for the project:

#define straight 1
#define right 2
#define left 3

With the setup out of the way, we're 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 CupertinoViewController methods. The viewDidLoad method will give us a good place to load all three of our sound files and set the soundStraight, soundRight, soundLeft references appropriately. We'll also use it to initialize the lastSound variable to 0, which doesn't match any of our sound constants. This ensures that whatever the first sound is, it will play.

Edit CupertinoViewController.m and update viewDidLoad to match Listing 21.4.

Listing 21.4

- (void)viewDidLoad {
    [super viewDidLoad];
 
    NSString *soundFile;
 
    soundFile = [[NSBundle mainBundle]
                  pathForResource:@"straight" ofType:@"wav"];
    AudioServicesCreateSystemSoundID((CFURLRef)
                [NSURL fileURLWithPath:soundFile]
                ,&soundStraight);
    [soundFile release];
 
    soundFile = [[NSBundle mainBundle]
                  pathForResource:@"right" ofType:@"wav"];
    AudioServicesCreateSystemSoundID((CFURLRef)
                [NSURL fileURLWithPath:soundFile]
                ,&soundRight);
    [soundFile release];
 
    soundFile = [[NSBundle mainBundle]
                  pathForResource:@"left" ofType:@"wav"];
    AudioServicesCreateSystemSoundID((CFURLRef)
                [NSURL fileURLWithPath:soundFile]
                ,&soundLeft);
    [soundFile release];
    lastSound=0;
 
    locMan = [[CLLocationManager alloc] init];
    locMan.delegate = self;
    locMan.desiredAccuracy = kCLLocationAccuracyThreeKilometers;
    locMan.distanceFilter = 1609; // a mile
    [locMan startUpdatingLocation];
    if ([CLLocationManager headingAvailable]) {
        locMan.headingFilter = 15;
        [locMan startUpdatingHeading];
    }
}

Tip - Remember, this is all code we've used before! 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 CupertinoViewController.m method that implements this is locationManager:didUpdateHeading. Each time the arrow graphic is updated in this method, we'll prepare to play the corresponding sound with the AudioServicesPlay SystemSound function. Before we do that, however, we'll check to make sure it isn't the same sound as lastSound; this will help 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'll play it and update lastSound with a new value.

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

Listing 21.5

- (void)locationManager:(CLLocationManager *)manager
       didUpdateHeading:(CLHeading *)newHeading {
 
 
    if (self.recentLocation != nil && newHeading.headingAccuracy >= 0) {
        CLLocation *cupertino = [[[CLLocation alloc]
                                  initWithLatitude:kCupertinoLatitude
                                  longitude:kCupertinoLongitude] autorelease];
        double course = [self headingToLocation:cupertino.coordinate
                                        current:recentLocation.coordinate];
        double delta = newHeading.trueHeading - course;
        if (abs(delta) <= 10) {
            directionArrow.image = [UIImage imageNamed:@"up_arrow.png"];
           if (lastSound!=straight) AudioServicesPlaySystemSound(soundStraight);
            lastSound=straight;
        } else {
            if (delta > 180) {
                directionArrow.image = [UIImage imageNamed:@"right_arrow.png"];
                if (lastSound!=right) AudioServicesPlaySystemSound(soundRight);
                lastSound=right;
            } else if (delta > 0) {
                directionArrow.image = [UIImage imageNamed:@"left_arrow.png"];
                if (lastSound!=left) AudioServicesPlaySystemSound(soundLeft);
                lastSound=left;
            } else if (delta > -180) {
                directionArrow.image = [UIImage imageNamed:@"right_arrow.png"];
                if (lastSound!=right) AudioServicesPlaySystemSound(soundRight);
                lastSound=right;
            } else {
                directionArrow.image = [UIImage imageNamed:@"left_arrow.png"];
                if (lastSound!=left) AudioServicesPlaySystemSound(soundLeft);
                lastSound=left;
            }
        }
        directionArrow.hidden = NO;
    } else {
        directionArrow.hidden = YES;
    }
 
}

The application is now ready for testing. Use Build and Run to install the updated Cupertino application on your iPhone, 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's plist file to contain the Required Background Modes (UIBackgroundModes) key.

Tip - If, while you're testing the application, it still seems a bit "chatty" (playing the sounds too often), you may want to update the 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 Key

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 designations to the application for it to work properly. Update the Cupertino project plist by following these steps:

  1. Click to open the project's plist file in the resources group (Cupertino-Info.plist).
  2. Add an additional key to the property list, selecting Required Background Modes (UIBackgroundModes) from the Key pop-up menu.
  3. Expand the key and add two values within it: App Plays Audio (Audio) and App Registers for Location Updates (Location), as shown in Figure 21.7. Both values will be selectable from the pop-up menu in the Value field.
  4. Save the changes to the plist file.

21fig07.jpg

Figure 21.7 Add the background modes that are required by your application.

After updating the plist, install the updated application on your iPhone 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.

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

Completing a Long-Running Background Task

In our final 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. Sure, we could demonstrate how to add code to an existing project and allow a method to run in the background, but we don't have any long running methods that could make use of it.

To demonstrate how we can tell iOS to allow something to run in the background, we'll create a new application, SlowCount, that does nothing but count to 1,000—slowly. We'll 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 21.8.

21fig08.jpg

Figure 21.8 To simulate a long-running task, our application will count. Slowly.

Preparing the Project

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

The application will have a single outlet, a UILabel named theCount, which we'll use to present the counter onscreen. In addition, it will need a an integer to use as a counter (count), an NSTimer object that will trigger the counting at a steady interval (theTimer), and a UIBackgroundTaskIdentifier variable (not an object!) that we'll use to reference the task we have running in the background (counterTask).

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

Open the SlowCountViewController.h file and implement it as shown in Listing 21.6.

Listing 21.6

#import <UIKit/UIKit.h>
 
@interface SlowCountViewController : UIViewController {
    int count;
    NSTimer *theTimer;
    UIBackgroundTaskIdentifier counterTask;
    IBOutlet UILabel *theCount;
}
 
@property (nonatomic,retain) UILabel *theCount;
 
@end

Note - The UILabel theCount is the only object we'll be accessing and modifying properties of in the application; therefore, it is the only thing that needs a @property declaration.

Next, clean up after the UILabel object in the SlowCountViewController.m dealloc method. The other instance variables either aren't objects (count, counterTask) or will be allocated and released elsewhere in the application (NSTimer):

- (void)dealloc {
    [theCount release];
    [super dealloc];
}

Creating the User Interface

It's a bit of a stretch to claim that this application has a "user interface," but we still need to prepare the SlowCountViewController.xib to show the theCount label on the screen.

Open the XIB file in Interface Builder, and drag a UILabel into the center of the view. Set the label's text to read 0. With the label selected, use the Attributes Inspector (Command+1) to set the label alignment to center and the font size to 36. 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 21.9.

Finish the view by Control-dragging from the File's Owner icon in the Document window to the UILabel in the view. Choose theCount when prompted to make the connection.

21fig09.jpg

Figure 21.9 Add a UILabel to the view to hold the current count.

Implementing the Counter Logic

To finish our applications core functionality (counting!), we need to do two things. First, we need to set the counter (count) to 0 and allocate and initialize NSTimer that will fire at a regular interval. Second, when the timer fires, we will ask it to invoke a second method, countUp. In the countUp method, we'll check to see whether count is 1000. If it is, we'll turn off the timer and we're done, if not, we'll update count and display it in our UILabel theCount.

Initializing the Timer and Counter

Let's start with initializing the counter and timer. What better place to do this than in SlowCount.m's viewDidLoad method. Implement viewDidLoad as shown in Listing 21.7.

Listing 21.7

1: - (void)viewDidLoad {
2:     [super viewDidLoad];
3:     count=0;
4:     theTimer=[NSTimer scheduledTimerWithTimeInterval:0.1
5:                     target:self
6:                     selector:@selector(countUp)
7:                     userInfo:nil
8:                     repeats:YES];
9: }

Line 3 initializes our integer counter, count, to 0.

Lines 4–8 initialize and allocate the theTimer NSTimer object with an interval of 0.1 seconds. The selector is set to use the method countUp, which we'll be writing next. The timer is set to keep repeating with repeats:YES.

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 21.8, before the viewDidLoad method in SlowCountViewController.m. This should be quite straightforward—if the count equals 1000, we're done and it's time to clean up—otherwise, we count!

Listing 21.8

 1: - (void)countUp {
 2:     if (count==1000) {
 3:         [theTimer invalidate];
 4:         [theTimer release];
 5:     } else {
 6:         count++;
 7:         NSString *currentCount;
 8:         currentCount=[[NSString alloc] initWithFormat:@"%d",count];
 9:         theCount.text=currentCount;
10:         [currentCount release];
11:     }
12: }

Lines 2–4 handle the case that we've reached the limit of our counting (count==1000). If it has, we use the timer's invalidate method to stop it, and then release it.

Lines 5–11 handle the actual counting and display. Line 5 updates the count variable. Line 7 declares the currentCount string, which is then allocated and populated in line 8. Line 9 updates our theCount label with the currentCount string. Line 10 releases the string object.

Build and 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'll use this code snippet to mark the beginning of the code we want to execute in the background:

counterTask = [[UIApplication sharedApplication]
              beginBackgroundTaskWithExpirationHandler:^{
                 // If you're worried about exceeding 10 minutes, handle it here
              }];

And we'll use this code snippet to mark the end:

[[UIApplication sharedApplication] endBackgroundTask:counterTask];

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

Let's update our viewDidLoad and countUp methods to include these code additions. In viewDidLoad, we'll 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 and released.

Update viewDidLoad as shown in Listing 21.9.

Listing 21.9

- (void)viewDidLoad {
    [super viewDidLoad];
 
    counterTask = [[UIApplication sharedApplication]
              beginBackgroundTaskWithExpirationHandler:^{
                 // If you're worried about exceeding 10 minutes, handle it here
              }];
    count=0;
    theTimer=[NSTimer scheduledTimerWithTimeInterval:0.1
                    target:self
                    selector:@selector(countUp)
                    userInfo:nil
                    repeats:YES];
}

Then make the corresponding additions to countUp, demonstrated in Listing 21.10.

Listing 21.10

- (void)countUp {
    if (count==1000) {
        [theTimer invalidate];
        [theTimer release];
        [[UIApplication sharedApplication] endBackgroundTask:counterTask];
    } else {
        count++;
        NSString *currentCount;
        currentCount=[[NSString alloc] initWithFormat:@"%d",count];
        theCount.text=currentCount;
        [currentCount release];
    }
}

Save your project files, then Build and Run the application on your iPhone or in the simulator. After the counter starts counting, pressing the Home button to move the application to the background. Wait a minute or so, and then re-open 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!

Further Exploration

When I sat down to write this lesson, I was torn. Background tasks/multitasking is definitely the "must have" feature of iOS 4.0, 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 iPhone battery life and speed.

As a next step, you should read the following sections in Apple's iPhone Application Programming Guide (available through the Xcode documentation): "Executing Code in the Background," "Preparing Your Application to Execute in the Background," and "Initiating Background Tasks."

As you review Apple's documentation, pay close attention to the tasks that your application should be completing as it enters 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 a well-defined set of rules that iOS background-enabled applications you must follow to be considered "good citizens" of iOS 4.x. In this hour's lesson, you learned about the different types of backgrounding available in iOS and the methods available to support background tasks. Over the course of five tutorial applications, you put these techniques to the test, creating everything from notifications triggered when an application isn't running to a simple navigation app that provides background voice prompting.

You should now be well prepared to create your own background-aware apps and take full advantage of the latest and greatest feature of iOS 4.0.

Q&A

  1. Why can't I run any code I want in the background?
  1. 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 is enormous. Remember that the iPhone is a phone. Apple intends to ensure that when your iPhone needs to be used as a phone, it functions as one!
  1. What about timeline-based background processing, like IM clients?
  1. Timeline-based processing (reacting to events that occur over time) is currently not allowed in iOS. This is a disappointment but ensures that there aren't dozens of apps sitting on your phone, eating up resources, waiting for something to happen.

Workshop

Quiz

  1. Background tasks can be anything you want in iOS 4.0. True or false?
  2. Any application you compile for iOS 4.0 will continue to run when the user exits it. True or false?
  3. Only a single long-running background task can be marked background completion. True or false?

Answers

  1. False. Apple has a well-defined set of rules for implementing background processing in iOS 4.0.
  2. False. Applications will suspend in the background by default. To continue processing, you must implement background tasks as described in this hour's lesson.
  3. False. You can mark as many long-running tasks as you'd like, but they must all complete within a set period of time (around 10 minutes).

Activities

  1. Return to a project in an earlier hour and properly enable it for background processing.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)