UPDATE: Added handleOpenURL to the flow charts. Added UIApplicationExitsOnSuspend. Untangled some lines.
UPDATE: renamed deprecated handleOpenURL to newer name.
Now that we all are moving our source code gradually to iOS 4 I had to pause and think a bit about where to move which code. A problem that I’m facing frequently when updating a project is that the didFinishLaunching is only called if the app really launches.
That poses a bit of a challenge if you are used to doing things like refreshing images or other files off the internet. An app that is resumed from standby does no longer go through this delegate method. So an app that would always show fresh content upon launch before 4.0 multitasking would no longer load any new content as soon as you build it for 4.0. That’s actually one of the main reasons why I have not yet had time to update MyAppSales to 4.0.
To gain the possibility for “fast app switching” you actually don’t need to do anything. All apps automatically support it because they no longer get terminated if the user pushes the Home button. They get put into a sleep mode while the iPhone still has enough memory for everything else. It’s only if RAM runs out that the OS starts killing apps. There is begins with the ones that have the most memory reserved.
I grabbed the free trial of Omnigraffle and the Non-techie Process Flowchart Stencils by gfraser. Then I researched when all these various delegate methods of UIApplication are being called and drew charts to illustrate the flow.
By inhaling first how it was before multitasking and then upgrading your mental process to backgrounding we can begin to fully appreciate how it all fits together.
The World Before Multitasking
The first thing that I learned from this is that applicationDidBecomeActive: is being called right after application:didFinishLaunchingWithOptions:. So even before 4.0 it would have been smart to put there any kind of code that you want executed whenever the app becomes active, be it after app start or upon rejecting a phone call.
Not shown on this diagram is the situation that your app is eating up all the system memory and ignores both the level 1 and level 2 memory warnings. In that case the OS will also terminate your app without totally unscrupulous.
The mechanism with WillResignActive and DidBecomeActive is sort of the Backgrounding Lite that has always been there. This occurs whenever some other thing is displayed in front of your app. This could be the UI to accept a phone call or something as benign as the synching screen.
Adding multitasking to this flow chart makes it about twice as complex. I probably spent an hour trying to fit it all together with no crossing lines. Another thing that I noticed while doing this is that applicationWillResignActive: is the only delegate method that I could not optimize into a single box, because one use is to show that the app has been interrupted, the other is to the path into the background.
An app built with 3.2 will still go through a short background stage where applicationDidEnterBackground: is called. but then it will be terminated as usual. If the same app is built against a 4.x SDK then it will not go straight from background to being terminated. Instead it will stick around and so will be the debugger. As soon as you tap the app icon again or choose it from the app switcher it is first informed that applicationWillEnterForeground and then applicationDidBecomeActive.
An app that is suspended but still in RAM might be terminated by the OS if memory runs low. But after entering the background there is no longer any delegate method being called, instead the app will receive a KILL signal. This occurs also if the user long-presses the app icon in app switcher and then removes the app from there with the removal button.
Accepting a phone call no longer terminates your app but will send it into the appropriate background mode. Within the background box you see two purple states that the app can be in. All apps are suspended in RAM per default. You might want to slim down your memory footprint to make your app a less likely target for killing if the system runs low on memory.
Only apps that actually require background processing continue to be executing. This requirement is either set in info.plist or by telling the OS that you have a task to complete, like for example a file upload.
The following kinds of activities get to run in true multi tasking behind the foreground app with lesser CPU priority.
- playing music: “App plays audio”
- geo location: “App registers for location updates”
- VOIP: “App provides Voice over IP services”
If your app does one of these things then adding the key to info.plist is all it takes. But be aware that as soon as your app stops doing what it stated it would be doing the OS again will send it to sleep, only to be awakend by another gentle tap by the user.
In background mode you’re advised to stop updating the UI. Especially with OpenGL Apple is really strict. If you app as much as touches OpenGL while in background the app gets killed right away.
There are only two scenarios left where your app’s applicationWillTerminate: will actually be called: if it’s build with an SDK of less than version 4.0 or if you chose to opt out of backgrounding by adding the UIApplicationExitsOnSuspend key to your info.plist.
So, let’s ask the question we’ve been doing all this fuss for: Where should you now put your code to initialize, update or save changes? applicationWillTerminate not being called in the majority of cases makes it a rather bad candidate to save user defaults or data.
Looking at the chart I see applicationDidEnterBackground as the ideal candidate delegate method for saving state. This is also the last method to be called before a regular 4.0 app gets put to sleep and before a possible kill.
For updating data from the web the case is not as clear. You would not want to reload every time the user comes back to your app. Possible applicationDidBecomeActive: is a good place, provided that you only reload data if sufficient time has passed to be able to assume that something new is available.
Only the first loading of data can stay in application:didFinishLaunchingWithOptions: since every call applicationDidBecomeActive: most likely has the data already loaded previously.
Adding fast app switching and multitasking support is quite easy since for the most part you don’t need to do anything. But you WILL have to review the contents of your app delegate methods to see which code has to be moved into one of the other delegate methods now. Clean out applicationWillTerminate: and have a close look at application:didFinishLaunchingWithOptions: to check if you have something in there that needs to be done every time the app becomes active.
These charts come from some experimentation that I did and what I remembered from the WWDC 2010 session videos on multitasking. Potentially there are mistakes or more surprises hidden in there. Please let me know if you find any.
Here’s the chart in PDF format in case you want to print it out and frame it.