- Understanding iOS 4 Backgrounding
- Disabling Backgrounding
- Handling Background Suspension
- Implementing Local Notifications
- Using Task-Specific Background Processing
- Completing a Long-Running Background Task
- Further Exploration
- Summary
- Q&A
- Workshop
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.
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).
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
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.
Figure 21.9 Add a UILabel to the view to hold the current count.
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.
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
: [currentCountrelease
];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
];
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; [currentCountrelease
]; } }
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!