Proper use of beginBackgroundTaskWithExpirationHandler

Eyal picture Eyal · Apr 25, 2012 · Viewed 52.4k times · Source

I'm a bit confused about how and when to use beginBackgroundTaskWithExpirationHandler.

Apple shows in their examples to use it in applicationDidEnterBackground delegate, to get more time to complete some important task, usually a network transaction.

When looking on my app, it seems like most of my network stuff is important, and when one is started I would like to complete it if the user pressed the home button.

So is it accepted/good practice to wrap every network transaction (and I'm not talking about downloading big chunk of data, it mostly some short xml) with beginBackgroundTaskWithExpirationHandler to be on the safe side?

Answer

Ashley Mills picture Ashley Mills · Apr 25, 2012

If you want your network transaction to continue in the background, then you'll need to wrap it in a background task. It's also very important that you call endBackgroundTask when you're finished - otherwise the app will be killed after its allotted time has expired.

Mine tend look something like this:

- (void) doUpdate 
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        [self beginBackgroundUpdateTask];

        NSURLResponse * response = nil;
        NSError  * error = nil;
        NSData * responseData = [NSURLConnection sendSynchronousRequest: request returningResponse: &response error: &error];

        // Do something with the result

        [self endBackgroundUpdateTask];
    });
}
- (void) beginBackgroundUpdateTask
{
    self.backgroundUpdateTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
        [self endBackgroundUpdateTask];
    }];
}

- (void) endBackgroundUpdateTask
{
    [[UIApplication sharedApplication] endBackgroundTask: self.backgroundUpdateTask];
    self.backgroundUpdateTask = UIBackgroundTaskInvalid;
}

I have a UIBackgroundTaskIdentifier property for each background task


Equivalent code in Swift

func doUpdate () {

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {

        let taskID = beginBackgroundUpdateTask()

        var response: URLResponse?, error: NSError?, request: NSURLRequest?

        let data = NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error: &error)

        // Do something with the result

        endBackgroundUpdateTask(taskID)

        })
}

func beginBackgroundUpdateTask() -> UIBackgroundTaskIdentifier {
    return UIApplication.shared.beginBackgroundTask(expirationHandler: ({}))
}

func endBackgroundUpdateTask(taskID: UIBackgroundTaskIdentifier) {
    UIApplication.shared.endBackgroundTask(taskID)
}