Handling App Lifecycle In SwiftUI With scenePhase

April 25th, 2025

⏱ Reading Time: 4 mins

When building apps, we often reach a point where it's important to be aware of transitions in the app lifecycle. We may need, for instance, to start timers or long-running tasks when the app comes into the foreground, to stop them when it becomes inactive, or to persistently store data when it enters the background. Quite often we rely on UIKit's AppDelegate to determine the current state of an app. However, SwiftUI offers a much cleaner and modern solution; the scenePhase environment value.

Let's get to know it.

The scenePhase and the scene lifecycle values

Usually, most SwiftUI apps are parted by a single scene with multiple views, although they can also contain multiple scenes. Depending on where we "read" it, the scenePhase environment value reports either the state of one, or all scenes in the app —more about that next. If the app has one scene only, then changes to its state reflect changes to the app's state too.

Regardless, scenePhase provides three different values that describe the current lifecycle state of an app's scene:

  • active: The app is currently in the foreground and interactive.
  • inactive: The app is still in the foreground but not interacting. For example, when the device is receiving a call, or when transitioning to another app.
  • background: The app is no longer visible and is running in the background. According to Apple docs, it's safe to assume that the app might be terminated soon after it enters that state. Therefore, it's extremely important to persistently store any data remaining in memory, or perform other necessary finishing tasks.

We can use scenePhase for a wide range of tasks, with the following being some common use cases:

  • Saving data when the app moves to the background.
  • Pausing animations or timers while inactive.
  • Resuming long-running tasks when returning to the foreground.

Using the scenePhase environment value

Let's see how we can use the scenePhase environment value. Just like all environment values in SwiftUI, we import it to any view with the following declaration:

For the sake of the demonstration, let's assume the following simple view:

The easiest way to detect changes to scenePhase and react to them is to use the onChange view modifier. Then, in a switch statement we can handle each case separately, or use if statements to handle only cases we are interested in.

For instance, the following code updates the statusText state property according to the new phase value and prints it to the console:

Here's the above in action:

It's important to highlight the following:

When observing scenePhase inside a SwiftUI view, just like it's demonstrated here, then we do so only for the scene that the view belongs to. That's fine if the app has one scene only. The next part explains what to do when having multiple scenes.

For your reference, here's the entire code of the view:

Using scenePhase in the App struct

We can also monitor lifecycle changes at the app level, in the main App struct of the project. By doing that, we are not watching for changes to one scene only, but to all scenes in the app.

In that case, scenePhase has:

  • the active value when any of the scenes is active,
  • the inactive value when all scenes are inactive,
  • the background value when all scenes have entered the background.

The following example demonstrates that:

Conclusion

The scenePhase environment value gives us control over our app's lifecycle in an elegant and straightforward manner. It's a pure SwiftUI solution, so there is no need to resort to UIKit's AppDelegate and get into unnecessary hassle. The few simple examples of this post demonstrate the various phases, how scenePhase is usually treated, and where to use it depending on whether an app has one or multiple scenes. Undeniably, it's a great tool for keeping our UI and data in sync with the app’s current state. Thank you for reading!

Stay Up To Date

Subscribe to my newsletter and get notifiied instantly when I post something new on SerialCoder.dev.

    We respect your privacy. Unsubscribe at any time.