Simplifying iOS background tasks
On iOS, leaving an app or locking the device triggers your app to go from the Active
state, to Background
, and promptly, Suspended
. The suspended state suspends any code execution and frees up resources for other apps, which is a good thing.
Often, though, we need a little more time for our app to clean up. This is where Background Tasks come into play. Using Background Tasks, iOS will give your app a couple minutes to keep working, before it becomes Suspended.
Imagine your app allows people to take photos and send them to friends. Users might snap a photo, select a recipient, hit send & immediately leave your app. Without a Background Task, that photo may never make it.
Adding background tasks should be easy, but in fact, it’s ugly and cumbersome.
Here is what a normal background task setup looks like:
// Create the background task
__block UIBackgroundTaskIdentifier bgTask;
UIApplication *app = [UIApplication sharedApplication];bgTask = [app beginBackgroundTaskWithName:nil expirationHandler:^{ // End the task so the OS doesn’t kill the app
if (bgTask != UIBackgroundTaskInvalid) {
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}
}}];/* ... Code permitted to execute in background … */// End the task
if (bgTask != UIBackgroundTaskInvalid) {
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}
It’s a lot to swallow. That’s why I made a category on UIApplication
to make it simple.
Here is the same functionality using CFABackgroundTask:
CFABackgroundTask *task = [UIApplication cfa_backgroundTask];/* … Code permitted to execute in background … */[task invalidate];
Optionally, if you need to execute some code upon the task’s expiration, you can provide an expiration block via cfa_backgroundTaskWithExpiration:
.
Test it out
Here’s a simple example to try. In your AppDelegate.m
implement the following in applicationDidEnterBackground:
:
// Start the background task
CFABackgroundTask *task = [UIApplication cfa_backgroundTask];// Wait 5 seconds…
dispatch_time_t delayTimer = dispatch_time(DISPATCH_TIME_NOW, 5.0 * NSEC_PER_SEC);
dispatch_after(delayTimer, dispatch_get_main_queue(), ^(void){ NSLog(@”Application state: %ld”, [[UIApplication sharedApplication] applicationState]);
NSLog(@”Background Time Remaining: %0.1f”, [[UIApplication sharedApplication] backgroundTimeRemaining]); // End the task
[task invalidate];
});
With the CFABackgroundTask
, you’ll see the logs print 5s after entering background. When run without it, those logs aren’t printed — not until you open the app back up.
(NOTE: Be sure to run on a device — the simulator doesn’t always suspend apps like a real device does)
I like to use background tasks often, and I especially like to optimize my development. Whether scheduling local notifications, resizing images, or saving data, CFABackgroundTask
is a simple and useful way to make sure your app finishes the job as the user expects.
Check it out — https://github.com/bradgmueller/CFABackgroundTask