Appendix A. Application Lifetime Events

When your app launches, its one and only application object is created for you (by UIApplicationMain) as the shared application object, together with its delegate, adopting the UIApplicationDelegate protocol. The application reports lifetime events to its delegate through calls to protocol-defined methods; other instances can also register to receive most of these events as notifications.

These events, notifying you of stages in the lifetime of your app as a whole and giving your code an opportunity to run in response, are extraordinarily important and fundamental. This appendix is devoted to a survey of them, along with some typical scenarios in which they will arrive.

Application States

In the early days of iOS — before iOS 4 — the lifetime of an app was extremely simple: either it was running or it wasn’t. The user tapped your app’s icon in the springboard, and your app was launched and began to run. The user used your app for a while. Eventually, the user pressed the Home button (the physical button next to the screen) and your app was terminated — it was no longer running. The user had quit your app. Launch, run, quit: that was the entire life cycle of an app. If the user decided to use your app again, the whole cycle started again.

The reason for this simplicity was that, before iOS 4, an iOS device, with its slow processor and its almost brutal paucity of memory and other resources, compensated for its own shortcomings by a simple rule: it could run only one app at a time. While your app was running, it occupied not only the entire screen but the vast majority of the device’s resources, leaving room only for the system and some hidden built-in processes to support it; it had, in effect, sole and complete control of the device.

Starting in iOS 4, that changed. Apple devised an ingenious architecture whereby, despite the device’s limited resources, more than one app could run simultaneously — sort of. The Home button changed its meaning and its effect upon your app: contrary to the naïve perception of some users, the Home button was no longer a Quit button. Nowadays, when the user presses the Home button to leave your app, your app does not die; technically, the Home button does not terminate your app. When your app occupies the entire screen, it is in the foreground (or frontmost); when some other app proceeds to occupy the entire screen, your app is backgrounded and suspended. This means that your app is essentially freeze-dried; its process still exists, but it isn’t actively running, and it isn’t getting any events — though notifications can be stored by the system for later delivery if your app comes to the front once again.

The cleverness of this arrangement is that your app, when the user returns to it after having left it to use some other app for a while, is found in the very same state as when the user left it. The app was not terminated; it simply stopped and froze, and waited in suspended animation. Returning to your app no longer means that your app is launched, but merely that it is resumed.

All of this is not to say, however, that your app can’t be terminated. It can be — though not by the user pressing the Home button. For example, the user might switch off the device; that will certainly terminate your app. The most common scenario, however, is that the system quietly kills your app while it is suspended. This undermines the app’s ability to resume; when the user returns to your app, it will have to launch from scratch, just as in the pre–iOS 4 days. The death of your app is rather like that of the scientists killed by HAL 9000 in 2001: A Space Odyssey — they went to sleep expecting to wake up later, but instead their life-support systems were turned off while they slept. The iOS system’s reasons for killing your app are not quite as paranoid as HAL’s, but they do have a certain Darwinian ruthlessness: your app, while suspended, continues to occupy a chunk of the device’s memory, and the system needs to reclaim that memory so some other app can use it.

When the user leaves your app, therefore, one of two things might happen later. It could be woken and resumed from suspended animation, in the very state that it was in when the user left it, or it could be launched from scratch because it was terminated in the background. It is this bifurcation of your app’s possible fates that state saving and restoration, discussed at the end of Chapter 6, is intended to cope with. The idea, in theory, is that your app should behave the same way when it comes to the front, regardless of whether it was terminated or merely suspended. We all know from experience, however, that this goal is difficult to achieve, and Apple’s own apps are noteworthy for failing to achieve it; for example, when Apple’s iBooks app comes to the front, it is perfectly obvious from its behavior and appearance whether it was terminated or merely suspended in the background.

A further complication is that your app can be backgrounded without being suspended. This is a special privilege, accorded in order that your app may perform a limited range of highly focussed activities. For example, an app that is playing music or tracking the device’s location when it goes into the background may be permitted to continue doing so in the background. In addition, an app that has been suspended can be woken briefly, remaining in the background, in order to receive and respond to a message — in order to update its data via the network, for example, or to be told that the user has crossed a geofence. (See Chapters 14, 21, and 23.)

There is also an intermediate state in which your app can find itself, where it is neither frontmost nor backgrounded. This happens, for example, when the user summons the control center or notification center in front of your app — or, starting in iOS 9, during iPad multitasking. In such situations, your app may be inactive without actually being backgrounded.

Your app’s code can thus, in fact, be running even though the app is not frontmost. If your code needs to know the app’s state in this regard, it can ask the shared UIApplication object for its applicationState (UIApplicationState), which will be one of these:

  • .active

  • .inactive

  • .background

Note

Your app can opt out of background suspension: you set the “Application does not run in background” key (UIApplicationExitsOnSuspend) in your Info.plist, and now the Home button does terminate your app, just as in the pre–iOS 4 days. It’s improbable that you would want to do this, but it could make sense for some apps.

App Delegate Events

The suite of basic application lifetime events that may be sent to your app delegate is surprisingly limited; indeed, in my opinion, the information your app is given is unfortunately rather too coarse-grained. The events are as follows:

application(_:didFinishLaunchingWithOptions:)

The app has started up from scratch. You’ll typically perform initializations here. If an app doesn’t have a main storyboard, or is ignoring the main storyboard at launch time, this code must also ensure that the app has a window, set its root view controller, and show the window (see Appendix B).

(Another event, application(_:willFinishLaunchingWithOptions:), arrives even earlier. Its purpose is to allow your app to participate in the state saving and restoration mechanism discussed in Chapter 6.)

applicationDidBecomeActive(_:)

Received after application(_:didFinishLaunchingWithOptions:). Also received after the end of the situation that caused the app delegate to receive applicationWillResignActive(_:).

applicationWillResignActive(_:)

One possibility is that something has blocked the app’s interface — for example, the screen has been locked. A local notification alert or an incoming phone call could also cause this event. So too, in iOS 9 and later, can an iPad multitasking situation. When this situation ends, the app delegate will receive applicationDidBecomeActive(_:).

Alternatively, the app may be about to go into the background (and will then probably be suspended); in that case, the next event will be applicationDidEnterBackground(_:).

applicationDidEnterBackground(_:)

The application has been backgrounded. Always preceded by applicationWillResignActive(_:).

Your app will then probably be suspended; before that happens, you have a little time to finish up last-minute tasks, such as relinquishing unneeded memory (see Chapter 6), and if you need more time for a lengthy task, you can ask for it (see Chapter 24).

applicationWillEnterForeground(_:)

The application was backgrounded, and is now coming back to the front. Always followed by applicationDidBecomeActive(_:). Note that this message is not sent on launch, because the app wasn’t previously in the background.

applicationWillTerminate(_:)

The application is about to be killed dead. Surprisingly, however, even though every running app will eventually be terminated, it is extremely unlikely that your app will ever receive this event (unless it has opted out of background suspension, as I explained earlier). The reason is that, by the time your app is terminated by the system, it is usually already suspended and incapable of receiving events. (I’ll mention some exceptional cases, though, in the next section; and see Chapter 14 for another.)

App Lifetime Scenarios

The application lifetime events are best understood through some typical scenarios:

The app launches from scratch

Your app delegate receives these messages:

  • application(_:didFinishLaunchingWithOptions:)

  • applicationDidBecomeActive(_:)

The user clicks the Home button

If your app was frontmost, your app delegate receives these messages:

  • applicationWillResignActive(_:)

  • applicationDidEnterBackground(_:)

The user summons your backgrounded app to the front

Your app delegate receives these messages:

  • applicationWillEnterForeground(_:)

  • applicationDidBecomeActive(_:)

If the user summons your backgrounded app to the front indirectly, another delegate message may be sent between these two calls. For example, if the user asks another app to hand a file over to your app (Chapter 22), your app delegate receives the application(_:open:options:) call between applicationWillEnterForeground(_:) and applicationDidBecomeActive(_:).

The user double-clicks the Home button

The user can now work in the app switcher interface. If your app is frontmost, your app delegate receives this message:

  • applicationWillResignActive(_:)

The user, in the app switcher, chooses another app

If your app was frontmost, your app delegate receives this message:

  • applicationDidEnterBackground(_:)

The user, in the app switcher, chooses your app

If your app was the most recently frontmost app, then it was never backgrounded, so your app delegate receives this message:

  • applicationDidBecomeActive(_:)

The user, in the app switcher, terminates your app

If your app was the most recently frontmost app, your app delegate receives these messages:

  • applicationDidEnterBackground(_:)

  • applicationWillTerminate(_:)

This is one of the few extraordinary circumstances under which your app can receive applicationWillTerminate(_:), because it was never backgrounded long enough to be suspended.

The user summons the control center or notification center

If your app is frontmost, your app delegate receives this message:

  • applicationWillResignActive(_:)

The user dismisses the control center or notification center

If your app is frontmost, your app delegate receives this message:

  • applicationDidBecomeActive(_:)

A local notification alert appears

If your app is frontmost, your app delegate receives this message:

  • applicationWillResignActive(_:)

From a local notification alert, the user launches the other app

Your app delegate receives this message:

  • applicationDidEnterBackground(_:)

On a multitasking iPad, the user splits the screen to show the app switcher

Your app delegate receives this message:

  • applicationWillResignActive(_:)

On a multitasking iPad, the user splits the screen to show two apps

Your app delegate, if it received applicationWillResignActive:, receives this message:

  • applicationDidBecomeActive(_:) (both apps are active when they share the screen)

On a multitasking iPad, the user toggles between split sizes

Your app delegate receives these messages:

  • applicationWillResignActive(_:) (the app is inactive while the user is dragging the divider)

  • applicationDidBecomeActive(_:)

The screen is locked

If your app is frontmost, your app delegate receives these messages:

  • applicationWillResignActive(_:)

  • applicationDidEnterBackground(_:)

The screen is unlocked

If your app is frontmost, your app delegate receives these messages:

  • applicationWillEnterForeground(_:)

  • applicationDidBecomeActive(_:)

The user holds down the screen-lock button

The device offers to shut itself off. If your app is frontmost, your app delegate receives this message:

  • applicationWillResignActive(_:)

The user, as the device offers to shut itself off, cancels

If your app is frontmost, your app delegate receives this message:

  • applicationDidBecomeActive(_:)

The user, as the device offers to shut itself off, accepts

If your app is frontmost, the app delegate receives these messages:

  • applicationDidEnterBackground(_:)

  • applicationWillTerminate(_:)

Bear in mind that you can’t make any assumptions about the timing of the app delegate messages with respect to lifetime events received by other objects. They may well be interwoven in surprising ways. For example, there are circumstances where the root view controller may receive its initial lifetime events, such as viewDidLoad: and viewWillAppear(_:), before application(_:didFinishLaunchingWithOptions:) has even finished running.

Different systems can also introduce changes in timing. For example, ever since I start programming iOS, I relied upon the opening sequence of events, involving the app delegate and the root view controller, arriving in this order:

  1. application(_:didFinishLaunchingWithOptions:)

  2. viewDidLoad

  3. viewWillAppear(_:)

  4. applicationDidBecomeActive(_:)

  5. viewDidAppear(_:)

Therefore, I would typically use the root view controller’s viewDidAppear(_:) to register for .UIApplicationDidBecomeActive in order to be notified of subsequent activations of the app. However, iOS 8 brought with it a momentous change: the app delegate receives applicationDidBecomeActive(_:) after the root view controller has received viewDidAppear(_:), like this:

  1. application(_:didFinishLaunchingWithOptions:)

  2. viewDidLoad

  3. viewWillAppear(_:)

  4. viewDidAppear(_:)

  5. applicationDidBecomeActive(_:)

This was a disaster for many of my apps, because the notification I just registered for in viewDidAppear(_:) arrived immediately afterward. In iOS 9, however, the order returned to what it was in iOS 7 and before. Needless to say, this difference alone can make writing an app compatible with iOS 8 a fairly daunting proposition.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.116.64.221