< Back

Call a Web Service as your iOS App Enters the Background

Albeit it confounding and slightly embarrassing to us developers, users do, on occasion, quit your app. Often times, your server may want to know about their temporal indecisiveness particularly if you need to clean up session data, record preference changes or send a notification asking for what possible reason they would leave your app and to recommend that they open it back up immediately (this may go against the App Store’s terms of service).

This arbitrarily quick tutorial shows you how to implement the appropriate UIApplicationDelegate method and hopefully call a HTTP endpoint.

1. Wire up the App Delegate

Our application’s delegate can implement a method called applicationDidEnterBackground. This method provides you with the ability to “request additional background execution time” and run tasks on a dispatch queue or secondary thread. Typically, you’re given 10 – 15 minutes to execute any processing, but if you go beyond that time threshold, your app will terminate.

AppDelegate.h

#import <UIKit/UIKit.h>

@interface AppDelegate : NSObject  {
    // Instance member of our background task process
    UIBackgroundTaskIdentifier bgTask; 
}

@end

AppDelegate.m

- (void)applicationDidEnterBackground:(UIApplication *)application {
    NSLog(@"Application entered background state.");

    // bgTask is instance variable
    NSAssert(self->bgTask == UIBackgroundTaskInvalid, nil);

    bgTask = [application beginBackgroundTaskWithExpirationHandler: ^{
        dispatch_async(dispatch_get_main_queue(), ^{
            [application endBackgroundTask:self->bgTask];
            self->bgTask = UIBackgroundTaskInvalid;
        });
    }];

    dispatch_async(dispatch_get_main_queue(), ^{
        
        if ([application backgroundTimeRemaining] > 1.0) {
            // Start background service synchronously
            [[BackgroundCleanupService getInstance] run];
        }
        
        [application endBackgroundTask:self->bgTask];
        self->bgTask = UIBackgroundTaskInvalid;
        
    });
}

There are couple key lines in the above implementation:

The first is the line bgTask = [application beginBackgroundTaskWithExpirationHandler…, which requests additional time to run clean up tasks in the background.

The second is the final code block of the delegate method beginning with dispatch_async. It’s basically checking whether there’s time left to run an operation via the call [application backgroundTimeRemaining]. In this example, I’m looking to run the background service once but alternatively, you can use a loop checking on the backgroundTimeRemaining on each iteration.

The line [[BackgroundCleanupService getInstance] run] will be a call to our singleton service class, which we’ll build right now.

2. Build the Background Service Class

With the app delegate ready to trigger our background task, we now need a service class that will communicate with the web server. In the following example, I’m going to a post a fictitious session key and parse a JSON encoded response. Also, I’m using two helpful libraries to make the request and deserialize the returned JSON, specifically JSONKit and ASIHttpRequest.

BackgroundCleanupService.h

#import <Foundation/Foundation.h>

@interface BackgroundCleanupService : NSObject

+ (BackgroundCleanupService *)getInstance;

- (void)run;

@end

BackgroundCleanupService.m

#import "BackgroundCleanupService.h"
#import "JSONKit.h"
#import "ASIHTTPRequest.h"

@implementation BackgroundCleanupService

/*
 * The singleton instance. To get an instance, use
 * the getInstance function.
 */
static BackgroundCleanupService *instance = NULL;

/**
 * Singleton instance.
 */
+(BackgroundCleanupService *)getInstance {
    @synchronized(self) {
        if (instance == NULL) {
            instance = [[self alloc] init];
        }
    }
    return instance;
}

- (void)run {

    NSURL* URL = [NSURL URLWithString:[NSString stringWithFormat:@"http://www.example.com/user/%@/endsession", @"SESSIONKEY"]];
    
    __block ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:URL];
    
    [request setTimeOutSeconds:20]; // 20 second timeout
    
    // Handle request response
    [request setCompletionBlock:^{
        NSDictionary *responseDictionary = [[request responseData] objectFromJSONData];
        
        // Assume service succeeded if JSON key "success" returned
        if([responseDictionary  objectForKey:@"success"]) {
        	    NSLog(@"Session ended");
        }
        else {
             NSLog(@"Error ending session");
        }
    }];
    
    // Handle request failure
    [request setFailedBlock:^{
        NSError *error = [request error];
        NSLog(@"Service error: %@", error.localizedDescription);
    }];
    
    // Start the request synchronously since the background service
    // is already running on a background thread
    [request startSynchronous];
}

@end

All the magic happens in the run method and by magic, I mean pretty boring and self-explanatory code. We’re making a GET request to a RESTful-style endpoint asking the server to end and clean up our session data. If the server does generate a valid response, we handle it within the [request setCompletionBlock? and if it fails, the [request setFailedBlock… gets called. The line [[request responseData] objectFromJSONData] decodes the JSON response and allows us to work with the data via a NSDictionary or a NSArray.

That’s it, now you’re ready to build more invasive apps!

Connect with us

Thank You!

We really appreciate your interest in what we do.

We'll get back to you as soon as we can.