iOS Tips and Tricks: Working in the Background

Sometimes it feels like there is nothing worse than when you're in an application and you are stuck waiting for it to do some long operation, such as downloading a file or doing some kind of long processing. It's understandable that some apps need to do such things, however, you should not be required to stay in the app while its performing such an operation.

For example, some of my favorite Magazine apps on the iPad have some large issues and they take some time to download. However, most of them require that i stay in the app. If I leave the app to check my mail the download stops until i re-enter the application. 

With iOS there really isn't a need to force a user through this kind of experience. Apple has API's that make it very easy to handle working in the background. So today i'm going to show you what you need to do, so that you can avoid making your users wait. Also, i'm going to provide you with a helper class that should make it even easier!

The first thing we'll need to do is create a private field to track our status:

UIBackgroundTaskIdentifier _bgTask;

Now, before we begin any long operation, we first need to call the following code:

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

What we're doing here, is essentially reseting our bgTask flag, and then subscribing to be notified when our application enters the background. 

Lets take a look at the implementation of the notification we created above:

- (void) doBackground:(NSNotification *)aNotification
{
   UIApplication *app = [UIApplication sharedApplication];

   if ([app respondsToSelector:@selector(beginBackgroundTaskWithExpirationHandler:)])
   {
      _bgTask = [app beginBackgroundTaskWithExpirationHandler:^
      {
         dispatch_async(dispatch_get_main_queue(), ^
         {
            if (_bgTask != UIBackgroundTaskInvalid)
            {
               [app endBackgroundTask:_bgTask];
               _bgTask = UIBackgroundTaskInvalid;
            }
         });
      }];
   }
}

In this handler, we're basically notifying the app that we are running task. 

Finally, once we've finished our task, we should then unsubscribe to our notification and if we're still in the background, we should notify the app, that we no longer have any tasks running in the background.

UIApplication *app = [UIApplication sharedApplication];
if ([app respondsToSelector:@selector(endBackgroundTask:)])
{
   if (_bgTask != UIBackgroundTaskInvalid)
   {
      [app endBackgroundTask:_bgTask];
      _bgTask = UIBackgroundTaskInvalid;
   }
}

Thats it! As simple as it is, its quite verbose code to be sticking everywhere in your application. So with that in mind, i wrote a little helper class that should make it a lot easier to manage. Essentially, all you'll need to do is call 2 methods:

@interface BackgroundManager : NSObject

-(void)startTask;
-(void)endTask;

@end

So, if you add the 2 files( BackgroundManager.h and BackgroundManager.m) to your project, you can use the code like this: 

#import "BackgroundManager.h"

@interface YourClass()
{
   BackgroundManager* _bgManager;
}
@end

@implementation YourClass

-(void)buttonClick
{
   if(_bgManager == nil)
      _bgManager = [[BackgroundManager alloc]init];

   [_bgManager startTask];
   // Do something that takes a while, such as download a file
}

-(void)finishedDoingLongTask
{

  // Whenever your'e done performing your task, call the endTask method
  [_bgManager endTask];
}

@end

And thats it!

You can download the files here:

Hope this was helpful. Check out my blog for more tips and tricks using iOS.

By Stephen Zaharuk (SteveZ)


Tags / ,