Pages

Sunday 2 October 2011

Application has active assertions beyond permitted time


If your application runs long running background task, then probably you would have seen several of these crash reports in your organizer's device logs.

"Application Specific Information:
<application_name> has active assertions beyond permitted time:
{(
    <SBProcessAssertion: 0x1fa7cef0> identifier: UIKitBackgroundCompletionTask process: OpenMobile[1087] permittedBackgroundDuration: 600.000000 reason: finishTask owner pid:1087 preventSuspend  preventIdleSleep ,
    <SBProcessAssertion: 0x1ed0ad70> identifier: UIKitBackgroundCompletionTask process: OpenMobile[1087] permittedBackgroundDuration: 600.000000 reason: finishTask owner pid:1087 preventSuspend  preventIdleSleep ,
    <SBProcessAssertion: 0x1fa96c80> identifier: UIKitBackgroundCompletionTask process: OpenMobile[1087] permittedBackgroundDuration: 600.000000 reason: finishTask owner pid:1087 preventSuspend  preventIdleSleep ,
)}"


What this tell is that your application is running (or has not marked a task as 'end') a background task beyond the permitted time. In order to understand this we should dive into the concept of long running background tasks.


Executing code in background on iOS

There are several ways to execute your code in background on iOS. If your applications supports one of the following background modes then it automatically gets a chance to run in background. Conditions applied...depending on the type of background mode your application can be woken up at regular intervals (voip mode), it can run until some interruption (audio mode) etc.

  • audio
  • location
  • voip
  • newsstand-content
  • external-accessory
  • bluetooth-central
  • bluetooth-peripheral

But what if your application does not fall in any of these category...don't you get a chance to run your code in background? Well...you do, but only for a certain amount of time. Apple decided that to be 10 minutes.

Executing a long running background task

So if your application does not fall in any of the categories mentioned above, you have to implement a long running task in order to run your code when your application transitions to background. You implement a long running task by calling this method:

[UIApplication sharedApplication] beingBackgroundTaskWithExpirationHandler:]

What this method does is that it tells iOS that iOS must not suspend your application immediately when it transitions to background rather give time and let your application finish the critical task application was running when it was in foreground. iOS gives your application only 10 minutes though to finish your task and mark that as finished when done using the following method:


[UIApplication sharedApplication] endBackgroundTask:]


This method marks the end of any long running background task. After 10 minutes iOS calls the expiration handler passed in the first method in order to give you chance to mark the end of the background task in case you have not already done so. This is how the general implmentation would look like:


UIApplication* application = [UIApplication sharedApplication];
    
UIBackgroundTaskIdentifier bgTaskId = UIBackgroundTaskInvalid;
if([application respondsToSelector:@selector(beginBackgroundTaskWithExpirationHandler:)]){
    bgTaskId = [application beginBackgroundTaskWithExpirationHandler:^{
        [application endBackgroundTask:bgTaskId];
    }];
}


You can see that the expiration handler marks the end of the background task. Once the expiration handler is called and after it exits, if there are any background task id which were created by your application and not have been marked as end, iOS TERMINATES your application and generates a crash report. The excerpt of such crash report is what is shown at the beginning of this post.

Solution

In order to solve this issue I have written a BackgroundTaskManager class - a simple class to manage background tasks. It exposes only one method:


-(UIBackgroundTaskIdentifier)beginNewBackgroundTask



How this works

The class maintains a list of all the background tasks created using the above method. When the expiration handler is called it goes through all the tasks one by one and marks each of them as finished by calling the above UIApplication's endBackgroundTask method.

3 comments:

  1. I found this background task manager in a project called Location (https://github.com/voyage11/Location). Whats the license for it so I can add it to the list of acknowledgements? It's an amazing piece of code.

    ReplyDelete
  2. Sorry Arjan for the late reply. It's under MIT license. I will also update the github repo with it.

    ReplyDelete
  3. Puru

    I know this is a very old blog, but the code is new to me a few weeks ago. I am finding it very useful but occasionally the background tasks expire for no reason that I can determine. I am trying to understand your code more so that I can get to the bottom of it. I hope to find someway of restarting the process if the master task does expire.

    Please can you explain the significance of the master task id being treated separately to the bgTaskIdList?

    Steve Brooker

    ReplyDelete