Monthly Archives: August 2010

Easy background Tasks in iOS 4

Now that my grouchy post about the problems with iOS 4 is out of the way, I thought it was time for a fun one. One of the apps I was working on when 4.0 came out had a download feature. Naturally, it made sense to continue downloading in a background task. But I wanted as few code changes as possible. Wrapping it all in start and end background task calls seemed excessive. And I had written my download class to be largely self-contained, and I did not like the idea of changing my code structure just to have it called by the app delegate methods. As often happens notifications made it easier.

The documentation briefly mentions the UIApplicationDidEnterBackgroundNotification as an alternative. Since my app was designed to run under 3.x as well (a quickly vanishing need), I ended up with this code in my download class init function:

        if ([[UIDevice currentDevice] respondsToSelector:@selector(isMultitaskingSupported)]) {
            bgTask = UIBackgroundTaskInvalid;
            [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(doBackground:)
                                                     name:UIApplicationDidEnterBackgroundNotification object:nil];
        }

Now, a nice non-UI class can easily respond correctly when the app goes into the background. Checking for a response from the UIDevice feels a little awkward, but it’s better than checking for version numbers directly. (Is there a way to check for the existence of a notification directly? It would feel cleaner.)

Now, what should we do when the notification happens? Well, if you simply want to continue your app for as long as Apple allows, here’s what your doBackground might look like:

- (void) doBackground:(NSNotification *)aNotification {
    UIApplication *app = [UIApplication sharedApplication];
    if ([app respondsToSelector:@selector(beginBackgroundTaskWithExpirationHandler:)]) {
         bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
            // Synchronize the cleanup call on the main thread in case
            // the task actually finishes at around the same time.
            dispatch_async(dispatch_get_main_queue(), ^{
                if (bgTask != UIBackgroundTaskInvalid)
                {
                    [app endBackgroundTask:bgTask];
                    bgTask = UIBackgroundTaskInvalid;
                }
            });
        }];
    }
}

You might recognize this from the App Programming guide. But I pared it down so this code only starts a background task, and does not end it until the very last minute. This way, whatever your code is doing, it will just keep doing it. (BTW, this might be a really bad time to call a synchronous download method. If your app is taking too long to respond on the main thread to these notifications, you might see the screen go black when coming back from backgrounding.)

If your program downloads as much as it can forever and ever, you are done. (OK, you’ve made a horrible, battery sucking app, but hey, your done!) Since I did not think my client would like that, I had to add 5 more easy lines. (Our app still got rejected, but that was not my fault I promise. And don’t ask, I won’t embarrass the client.) These lines check to see if the download is done, and end the task.

    UIApplication *app = [UIApplication sharedApplication];
    if (downloadArray.count == 0 &&  [app respondsToSelector:@selector(endBackgroundTask:)]) {
        if (bgTask != UIBackgroundTaskInvalid) {
            [app endBackgroundTask:bgTask];
            bgTask = UIBackgroundTaskInvalid;
        }
    }

This code is in with my method to check if there are any more downloads to be done. Now, when I’m done, I end cleanly, and stop drawing battery power when there’s no more work to be done. Frankly, I was surprised how easy this was. And I only had to change my existing code at initialization, and when downloads were complete. Maybe iOS4 isn’t so bad after all.

Advertisements

6 Comments

Filed under Uncategorized