More Comments on iOS 4 Multitasking

Some More Comments on iOS 4 Multitasking.

Expiration Handlers

If a process is running in background, having issued UIApplication's beginBackgroundTask before, then the process expiration handler's code following the (last) issuing of endBackgroundTask is not executed. In other words: iOS 4 does not wait until your expiration handler block is finishes. It waits for the last endBackgroundTask and terminates the app (quite ungracefully).

View Controllers

If your app enters background the active view controller does not receive a viewWillDisappear. Neither will it receive a viewWillAppear if the app enters foreground.

Application Lifecycle

On iOS 4.0 the message applicationWillTerminate is rarely send. I made some tests and found the follwoing:
  • An app will not receice applicationWillTerminate if it is in background and user selects force quit (pressing the minus sign in the list of recent apps).
  • An app will not receive applicationWillTerminate if is is in background and the user selects to shut down the device while your app is running.
  • An app will not receice applicationWillTerminate if it is in background and system shuts down due to low battery.
  • An app will receive applicationWillTerminate if it is in foreground and sytem shuts down due to low battery.
  • An app will receive applicationWillTerminate if it is in foreground and the user selects to shut down the device while your app is running.
If you like to save the state of your app you have to do so in applicationWillTerminate (for OS 2.x, 3.x and 4.x) and (!) in applicationDidEnterBackground (for OS 4.0).

A Short Note on iOS 4 (iPhone OS) Multitasking

Last weekend I checked out iOS 4, in particular its multitasking. From articles on the web I got the impression that iOS 4 multitasking isn't a real multitasking, rather a fast app switching (see, e.g., the article by Matt Neuburg as cited on daring fireball).

But: iOS 4 apps are fully multitasking with just two exceptions:
  • The iOS 4 UI event loop is single tasked, i.e. only the front app is running on the UI event loop. If app code is designed to be running on the UI event loop thread, then it is not executing if it enters background. However, this is not a big restriction. An app will not receive any UI events when running in background anyway (even on Mac OS X). If you design your iOS 4 app to be detached from the UI event loop it continues to run when put to background.
  • The OS may terminate your app when resources like memory are running low or "execution time" is used up. This is also not a big restriction. For example, on OS X if memory is running low the user is prompted to terminate an app. Also, on OS X I would terminate an app if it runs crazy and takes up all CPU time. So actually I believe it is an improvement to start thinking about rules when apps are terminated by the OS. (Note: For iOS 4 the rule which terminates an app is a bit too simple, as I will explain soon).

Apart from these restrictions you can run code in background. You can run your own run loop in background and register timers (events) with that run loop.

Background Run Loop

This is done with the following code:

  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

    // Create and register your timers

    // ...

    // Create/get a run loop an run it
    // Note: will return after the last timer's delegate has completed its job
    [[NSRunLoop currentRunLoop] run];
  });

In the upcoming release of Serial Mail (Version 4.1) I have implemented a background task which sends mail via an SMTP server in background. This background task uses timers to check the response of the SMTP server an hence it is requiring a run loop. I was able to do network connections in background. Apple says that you should prepare that network connections may fail when the app is running in background (however an app needs to check for failing connections anyway).

App Termination

So this solves the first problem, that the UI thread is single threaded. What about iOS terminating your app when it runs in background: To prevent that the OS terminates the app we should tell it that we have a long running background task. Apple suggest that you mark each background tasks by calls to UIApplication's beginBackgroundTask and endBackgroundTask see the developer documentation's sample code. I haven't seen a definition of "long running", but it appears as if the app is terminated if a background task runs 10 minutes after the app has been suspended (it does not help to launch a new background task / thread from another one).

Improving the App Termination Criteria

For my app 10 minutes background task time are sufficient. There are application where you require a truly long running background process checking stuff at certain intervals, etc. However, it is clear that the multitasking introduced in IOS 4 just needs a minor tweak: it requires more sophisticated rules for terminating apps. For apps with background task a much better rule would be to consider cpu time that real time. This would for example allow apps like instapaper to check and download files in background on a regular basis (say, once a day) if that task consumes only a small amount of cpu time. The author of instapaper discussed a different solution in his blog, however I hope for a simpler one: better, transparent criteria for termination of a background apps.

Scheduled Relaunch

Another improvement of the current multitasking APIs, which is more in line with the solution in the instapaper blog, would be to include a user configurable service for scheduled relaunch of an app into background. The service should be configurable on an per app basis. Apps registering with it should bring up a dialog requesting for permission (like location service does). The service should then call a method like applicationDidLaunchToBackground. I would prefer such a solution over a myriad of specialized background services. Instead the relaunch service could take options like "relaunch when network available".

There is one strange thing about iOS 4: When the user terminates the app manually (by deleting it from the list of recently used apps) then iOS does not call your app delegates applicationWillTerminate method. I have used applicationWillTerminate to save the application's state upon termination and now that code had to move to applicationWillResignActive.