TechnologiesSwiftUI

ScenePhase enum

iOSmacOStvOSwatchOSvisionOSiOS 14.0+✓ renders

An indication of a scene's operational state.

How it works

ScenePhase is an enumeration that describes the operational state of a scene or app — whether it is running and visible, momentarily interrupted, or no longer onscreen. SwiftUI publishes the current phase through the environment, so your views and scenes can observe lifecycle transitions declaratively instead of relying on app- or scene-delegate callbacks. Read it when you need to pause work, persist state, or release resources as the system foregrounds and backgrounds your app. Its cases — active, inactive, and background — give you a single, platform-agnostic signal for these moments.

  1. Read the phase from the environment with @Environment(\.scenePhase)

    SwiftUI keeps the current ScenePhase in the environment under the scenePhase key value. Declare an environment property to pull it into a view, and SwiftUI updates that property — and re-evaluates the body — whenever the phase changes. Here @Environment(\.scenePhase) private var scenePhase makes the live phase available throughout body.

  2. Switch over the cases: active, inactive, and background

    ScenePhase has three cases. active means the scene is in the foreground and interactive; inactive means it's visible but not receiving events (for example, mid-transition or partially obscured); and background means it's no longer onscreen. Because it's an enum, you pattern-match it directly — the example routes each case to a label in a switch newPhase block.

  3. Handle @unknown default for forward compatibility

    ScenePhase can gain cases in future SDKs, so an exhaustive switch should include an @unknown default clause. This lets the compiler warn you when new cases appear while still compiling against current and future systems. The example falls through to lastPhase = "unknown" for any case it doesn't recognize.

  4. React to transitions with onChange(of:)

    ScenePhase conforms to Equatable, so you can feed it to onChange(of:) to run side effects only when the phase actually moves — the natural place to checkpoint state or wind down activity. The example attaches .onChange(of: scenePhase) and uses the delivered newPhase to update what it displays; you'd put save-on-background logic in the .background branch.

Try it — Add a print(newPhase) call inside the .onChange(of: scenePhase) closure, then background and foreground the app to watch it report inactive on the way to background and back through to active.

Example & preview

Press Run live & edit to compile it in your browser — then edit the Swift on the left and the preview re-renders live.

ScenePhase.swift
struct ScenePhaseDemo: View {
    @Environment(\.scenePhase) private var scenePhase
    @State private var lastPhase = "active"

    var body: some View {
        VStack(spacing: 12) {
            Text("Scene Phase")
                .font(.headline)
            Text(lastPhase)
                .font(.title2.bold())
                .foregroundStyle(.tint)
            Text("Switch apps or lock the screen to see this change.")
                .font(.caption)
                .foregroundStyle(.secondary)
                .multilineTextAlignment(.center)
        }
        .padding()
        .onChange(of: scenePhase) { _, newPhase in
            switch newPhase {
            case .active: lastPhase = "active"
            case .inactive: lastPhase = "inactive"
            case .background: lastPhase = "background"
            @unknown default: lastPhase = "unknown"
            }
        }
    }
}
Live preview
Scene Phase active Switch apps or lock the screen to see this change.
Scene Phase active Switch apps or lock the screen to see this change.
swift → lexer → parser → sema → uiir → canvas Open in Studio ↗
What's new in SwiftUI 27 →