Image for post
Image for post

APP DEVELOPMENT

iOS App Lifecycle Explained

How the iOS lifecycle works, and how we can interact with it.

Riccardo Cipolleschi
Oct 27 · 7 min read

This week an interesting question has been raised in Bending Spoons (virtual) office about how the iOS Lifecycle works. To properly frame the problem, you need to know that we can interact with the lifecycle of iOS in two different ways:

  • By overriding some methods of the AppDelegate (like the usual application(_:didFinishLaunchingWithOptions:)) to respond to changes in the steps of the lifecycle.
  • By checking the UIApplication.shared.applicationState to know in which state the app is in a given moment in time. This enum can assume three different values: active, inactive and background. Note: this property can be accessed only from the main thread.

Now, the question was: what is the relationship between the UIApplication.State and the AppDelegate’s methods? How does the former changes when those methods are invoked?

Weirdly enough, we could not find any relevant piece of documentation that explains that relationship clearly. We found a lot of information about the app lifecycle. We found documentation on the UIApplication.State property, but we could not find a piece of documentation that put those together explicitly.

Therefore, I decided to set up a small experiment to learn how the two interact.

The Experiment

The experiment is simple, and it recalls a bit of what I did when I was exploring how much background time we have once we put the app in the background.

We want to create an elementary app that can collect the events fired by iOS when a lifecycle event occurs and that can associate to them the current value of the UIApplication.State property. We decorate the event with a timestamp to depict their timeline. The last requirement is to store them immediately on the disk to ensure we do not lose anything, especially when investigating edge cases.

The app's last step is the UI: I used a plain table view, where each event is rendered in a cell.

Preparing the Models

The models for this app are just two: we need to store the events in a state that can be encoded and stored on the disk. Every event has three property: a kind, a date and the associated UIApplication.State.

The code that represents these models is the following:

In the snippet, you can see a couple of extra details:

  1. The Event.Kind is an enumeration that contains all the lifecycle events we want to track.
  2. I added a couple of extensions that will help us create a new State starting from the previous one by adding a new event. And I added another init for the events.

Storing and Retrieving the State

Another essential ingredient for the app is a mechanism to:

  • store the State whenever it changes.
  • retrieve it when the app is launched.

For simplicity’s sake, I decided to use the UserDefault as a storage for the State.

The code to store and retrieve the state looks like this:

In this snippet, we create a static key to store the information in the UserDefaults. Then, in the retrieveState() method, we access the UserDefaults and we try to get and decode the data associated with that key. In case there is no data, we create a new struct for this launch.

In the store(state:) method, we encode the struct and we store it in the user defaults.

The app uses these methods in the AppDelegate: it defines a variable var state: State that, when set, automatically stores the new value in the UserDefaults. Then, it overrides the application(_:willFinishLaunchingWithOptions:) to create the state as soon as possible.

The code to glue everything together is the following.

At line 7, you can see that every time the state changes, we also update the state of the ViewController to reflect updates in the UI.

Tracking the Events

The last and most important piece of code consists of tracking all the events we are interested in. From the Event.Kind enum, we know that these events are:

  • appWillFinishLaunching
  • appDidFinishLaunching
  • appWillTerminate
  • appWillResignActive
  • appDidBecomeActive
  • appDidEnterBackground
  • appWillEnterForeground

we can easily override all these methods in the AppDelegate directly. The code looks like this:

The most interesting parts of this snippet are:

  • the application(_:didFinishLaunchingWithOptions:) method that has the responsibility to create a ViewController and make it visible on the screen
  • the updateState(with:event:) that packs together a couple of instructions that would have been repeated over and over otherwise.

Running the app

Now that everything is in place, we can run the app and see how the UIApplication.State changes with the evolution of the lifecycle events.

Image for post
Image for post

From this short video, you can see that the sequence of events when the app starts is:

  1. appWillFinishLaunching -> with a UIApplication.State of inactive
  2. appDidFinishLaunching -> with a UIApplication.State of inactive
  3. appDidBecomeActive -> with a UIApplication.State of active

Notice that, during all these events, the app is in the foreground and visible.

When, instead, we move the app in the background, and then we come back, the event sequence is:

  1. appWillResignActive -> with a UIApplication.State of active
  2. appDidEnterBackground -> with a UIApplication.State of background
  3. appWillEnterForeground -> with a UIApplication.State of background
  4. appDidBecomeActive -> with a UIApplication.State of active

Notice that, in this case, the app is never inactive but, instead, it passes from active to background and vice-versa. Also, notice that when the appWillEnterForeground is executed, the app is still in the background!

Edge Cases

There are a couple of edge cases that are worth investigating. What happens when the app is started in the background due to a silent notification or due to a background-fetch? What happens when the user invokes the app switcher?

Let’s explore these two cases!

Background Launch

Modern applications can be launched by the system to prepare the user's data as soon as they open the app or to download data from a server when new content is available.

All of this computation happens in the background, without the user even noticing it. The system launches the app, letting it perform some tasks for a fixed amount of time, and then it puts the app back to sleep. Which events are invoked? How the UIApplication.State evolves in those circumstances?

Luckily, the code we wrote so far is also suitable for this case. What we need to do is to simulate one of these events. To simulate a background fetch in Xcode we need to:

  1. Select the project in the Project Navigator
  2. Select the Signing and Capabilities tab
  3. Press the + Capability button
  4. Double-click on Background Modes
  5. In the new panel, select Background Fetch
Image for post
Image for post

These steps allow us to trigger a background fetch. Now, we just have to launch the app in that mode. We can do that by operating on the schema.

  1. Click on the app name, next to the Xcode play button.
  2. Select Edit Schema
  3. Select Options
  4. Check the Launch due to a background fetch option
Image for post
Image for post

Now, just launch the app. You won’t see the app pop up. Instead, you will see the icon appear in the simulator. Click on the icon to launch the app, and you’ll see something like this:

Image for post
Image for post

As you can see, in that case, both the willFinishLaunching and the didFinishLaunching have a UIApplication.State that is background while the only event which has active is when the app actually becomes active. Notice that the app is never inactive in this execution.

App Switcher

The last, interesting thing to analyze is what happens when the user opens the app switcher. Our code is already set up to track all the events so we just have to trigger the action in the simulator.

I found this step a little bit tricky: although the simulator said that the App Switcher can be triggered by pressing the^+⇧+⌘+H shortcut… It does not work every time. However, once you manage to pull it off, what you can see is that the first event triggered is the appWillResignActive event, with an associated value ofactive. If then we come back to the app, the following event is the appDidBecomeActive with a value of active.

Conclusion

In this article, we explored two mechanisms to interact with the app lifecycle. These mechanisms are different and, therefore, should be used for different tasks. However, they are related in a very strict way: when a lifecycle method is invoked, the UIApplication.State can change… and not always with a value we would expect.

The Startup

Medium's largest active publication, followed by +730K people. Follow to join our community.

Riccardo Cipolleschi

Written by

Hi everybody, I’m Riccardo. Senior iOS Engineer at Bending Spoons, I breathe iOS development: apps and tools. I love to share my knowledge with others.

The Startup

Medium's largest active publication, followed by +730K people. Follow to join our community.

Riccardo Cipolleschi

Written by

Hi everybody, I’m Riccardo. Senior iOS Engineer at Bending Spoons, I breathe iOS development: apps and tools. I love to share my knowledge with others.

The Startup

Medium's largest active publication, followed by +730K people. Follow to join our community.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store