Understanding the iOS Application Life Cycle iOS 06.11.2019

Every time you create an iOS project, it will likely include at least two .swift files: ViewController.swift and AppDelegate.swift. Where a project can have multiple ViewController.swift files connected to different scenes in a storyboard, a project will have one AppDelegate.swift file that contains code to manage the different states of an app. Initially, an app is not running. When the user launches the app, the app becomes active and appears in the foreground. As long as the user continues interacting with the app, it remains active in the foreground. However, if the user switches to another app, this pushes the other app into the background.

The AppDelegate.swift file monitors these different states so the app can respond accordingly. The app might also need to load any custom settings the user might have defined earlier such as color settings.

The various states an app might be in during its life cycle include:

  • Not Running. This is the state that all apps are on a freshly rebooted device.
  • Active. This is the normal running state of an application when it’s displayed on the screen to receive user input and update the display.
  • Background. In this state, an app is given some time to execute some code, but it can’t directly access the screen or get any user input. All apps enter this state briefly when the user presses the home button; most of them quickly move onto the Suspended state. Apps that want to do any sort of background processing stay in this state until they’re made Active again.
  • Suspended. A Suspended app is frozen. This is what happens to normal apps after their brief stint in the Background state. All the memory the app was using while it was active is held just as it was. If the user brings the app back to the Active state, it will pick up right where it left off. On the other hand, if the system needs more memory for whichever app is currently Active, any Suspended apps may be terminated (and placed back into the Not Running state) and their memory freed for other use.
  • Inactive. An app enters the Inactive state only as a temporary rest stop between two other states. The only way an app can stay Inactive for any length of time is if the user is dealing with a system prompt (such as those shown for an incoming call or SMS message) or if the user has locked the screen. This state is basically a sort of limbo.

Each state change serves different purposes:

  • Active > Inactive. Use applicationWillResignActive() to "pause" your app’s display. If your app is a game, you probably already have the ability to pause the gameplay in some way. For other kinds of apps, make sure no time-critical demands for user input are running because your app won’t be getting any user input for a while.
  • Inactive > Background. Use applicationDidEnterBackground() to release any resources that don’t need to be kept around when the app is tucked in the background (such as cached images or other easily reloadable data) or that might not survive backgrounding anyway (such as active network connections). Getting rid of excess memory usage here will make your app’s eventual Suspended snapshot smaller, thereby decreasing the risk that your app will be purged from RAM entirely. You should also use this opportunity to save any application data that will help your users pick up where they left off the next time your app is relaunched. If your app comes back to the Active state, normally this won’t matter; however, in case it’s purged and must be relaunched, your users will appreciate starting off in the same place. When this transition is underway, the system won’t give your app an unlimited amount of time to save any changes; it just gives you a few seconds. If your app takes longer than that, then your app will be purged from memory and pushed into the Not Running state.
  • Background > Inactive. Use applicationWillEnterForeground() to undo anything you did when switching from Inactive to Background. For example, here you can reestablish persistent network connections.
  • Inactive > Active. Use applicationDidBecomeActive() to undo anything you did when switching from Active to Inactive. Note that, if your app is a game, this probably does not mean dropping out of pause straight to the game; you should let your users do that on their own. Also keep in mind that this method and notification are used when an app is freshly launched, so anything you do here must work in that context as well.

When your app launches, the UIApplicationMain function creates its one and only UIApplication instance as the shared application object, along with the app delegate, which adopts the UIApplicationDelegate protocol.

To manage changes between states, the AppDelegate.swift file contains methods that its delegate can implement as follows:

  • application(_:didFinishLaunchingWithOptions:). Detects when an app starts running. 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
  • applicationDidBecomeActive(). Detects when an app, formerly in the background, reappears in the foreground once more. The app is now well and truly frontmost.
  • applicationWillResignActive(). Detects when the user returns to the Home screen, which will push the app into the background. The app is entering a situation where it is neither frontmost nor backgrounded; it will be inactive. Perhaps something has blocked the app’s interface - for example, the screen has been locked, or the user has summoned the notification center. A local notification alert or an incoming phone call could also cause this event. Whatever the cause, the app delegate will receive applicationDidBecomeActive(_:) when this situation ends.
  • applicationDidEnterBackground(). Detects when an app gets sent into the background. Always preceded by applicationWillResignActive(_:). Your app will now probably be suspended; before that happens, you have a little time to finish up last-minute tasks, such as relinquishing unneeded memory. In case you need additional time, you can request additional execution time from the system by calling beginBackgroundTask(expirationHandler:).
  • 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, even though every running app will eventually be terminated, it is quite unlikely that your app will ever receive this event. The reason is that, by the time your app is terminated by the system, it is usually already suspended and incapable of receiving events.

The applicationWillResignActive() and applicationDidBecomeActive() methods can be useful when detecting interruptions such as someone using your app when a phone call comes in and interrupts your app. This pair of methods brackets the movement of an app from the Active state to the Inactive state, which makes them good places to enable and disable any animations, in-app audio, or other items that deal with the app’s presentation to the user.

The two applicationDidEnterBackground() and applicationWillEnterForeground() methods deal with an app that is definitely being sent to the background. The applicationDidEnterBackground() method is where an app should free all resources such as saving all user data, closing network connections, and so forth. This is also the spot where you can request more time to run in the background if you need it, as we’ll see shortly. If you spend too much time doing things in applicationDidEnterBackground() – more than about 5 seconds – the system will decide that your app is misbehaving and terminate it.

You should implement applicationWillEnterForeground() to re-create whatever was torn down in applicationDidEnterBackground(), such as reloading user data, reestablishing network connections, and so on. Note that when applicationDidEnterBackground() is called, you can safely assume that applicationWillResignActive() has also been recently called. Likewise, when applicationWillEnterForeground() gets called, you can assume that applicationDidBecomeActive() will soon be called as well.

Finally, applicationWillTerminate(), which you’ll probably rarely use, if ever, is called only if your application is already in the background and the system decides to skip suspension for some reason and simply terminate the app.

Major State Changes

During very significant state changes, such as the app launching, being backgrounded, or coming back to the front, the app delegate receives a sequence of events:

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(_:)

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(_:)