How it works
The explanation, with examples you can run.
Two lifecycles: the process and its scenes
Since iOS 13, UIKit splits the lifecycle in two. The app delegate hears about the process: launch, push registration, memory warnings. Each scene (one instance of your UI, of which an iPad may show several) has its own states and its own delegate:
- Unattached: created by the system but not yet connected to UI.
- Foreground active: on screen and receiving touches.
- Foreground inactive: on screen but interrupted, for example by a system alert, Control Center or the app switcher.
- Background: off screen, still running code, briefly.
- Suspended: still in memory, but no code runs at all.
A scene can be disconnected while in the background to free memory, which sends it back to unattached; the process may live on. Apple's TN3187 says that iOS 18.4 began logging a warning for UIKit apps that haven't adopted scenes, and that in the next major release after iOS 26, apps built with the latest SDK must use the scene life cycle or they won't launch. SwiftUI apps already do.
What a transition gives you
@mainstruct NotesApp: App { @Environment(\.scenePhase) private var phase @State private var store = NoteStore() var body: some Scene { WindowGroup { NotesView(store: store) } .onChange(of: phase) { _, newPhase in if newPhase == .inactive || newPhase == .background { store.saveDraft() // cheap, synchronous, safe to repeat } } }}When a scene leaves the screen, Apple's guidance is to save user data, pause timers and pause games on deactivation, then release big caches, shared hardware such as the camera, and anything secret on screen once in the background. After your background callback returns, the system takes the snapshot shown in the app switcher, so hide sensitive views before then.
Suspended, then gone
A few seconds after reaching the background, the app is normally suspended: its memory is kept but its threads are frozen. From there, the system can end the process at any time to reclaim memory, and it does not wake you to say goodbye. applicationWillTerminate(_:) only runs if the app is still executing in the background when it is ended, so treat it as a bonus, never as your save point.
The next launch is a cold start. To make it feel as if nothing happened, restore what the person was looking at: @SceneStorage keeps small per-scene values such as the selected tab or a draft's ID, and your own store holds the data itself.
struct NotesView: View { @SceneStorage("selectedNote") private var selectedID: String? var store: NoteStore var body: some View { /* ... */ }}If you need a few more seconds to finish something important while moving to the background, ask for them before you get there (see the background execution topic).
Check yourself
3 questions. Get them all right to finish this topic.
0 of 3 right so far. Get all three to finish this topic.