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

6 responses to “Easy background Tasks in iOS 4

  1. Pingback: Easy Background Tasks at Under The Bridge

  2. tony

    thanks for this interesting article – i tried it and it (kind’a – need to practise ;)) worked!

    this may be interesting for you too (if not, I’m sorry….): http://creatingapps.telekomaustria.com/index.php
    there are some information about creatingapps

    have a nice day!

  3. JB

    Thanks for this article. I’m trying to implement it as well, but I’m having problem with your bgTask variable. How should it be initialized?
    I get an error because it’s not declared and I don’t quite get the mechanism of the multi-tasking.
    Thanks in advance.

    • Jason Rinn

      Sorry, I hope you got it figured out. Your comment got lost in all the spam I got at the same time.

      To answer your question, if you just add the bgTask variable to your class, the rest of the code posted should work just fine.

  4. Thank you! That solved my problems with Game Center disconnecting while multitasking during a multiplayer game.

  5. Thanks for this post!!

    It’s very clear and precise. Solved a huge issue I had!!

    Only thing: need to declare the bgTask variable before using it!!

    Thanks again!!

    Cheers,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s