Skip to content
beginner Phase 3 · App Lifecycle & Architecture

SceneDelegate & Multi-Window

Manage multiple windows and scenes on iPad, and handle scene lifecycle events.

35m
0 problems
Topic Progress 0%

Scene Lifecycle

What Is a Scene?

A Scene represents a single instance of your app's UI, typically a window. iOS 13+ uses the scene architecture to support multiple windows on iPad and handle system-managed scene lifecycle.

Each scene has its own UISceneDelegate (or UIWindowSceneDelegate) that responds to lifecycle events specific to that window.

Scene Lifecycle Methods

The UIScene protocol provides these key methods:

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    var window: UIWindow?
    
    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        // Scene is about to connect
        // Set up the window and root view controller
        guard let windowScene = scene as? UIWindowScene else { return }
        let window = UIWindow(windowScene: windowScene)
        window.rootViewController = UIHostingController(rootView: ContentView())
        self.window = window
        window.makeKeyAndVisible()
    }
    
    func sceneDidBecomeActive(_ scene: UIScene) {
        // Scene became active (foreground)
    }
    
    func sceneWillResignActive(_ scene: UIScene) {
        // Scene will become inactive
    }
    
    func sceneDidEnterBackground(_ scene: UIScene) {
        // Scene entered background
    }
    
    func sceneWillEnterForeground(_ scene: UIScene) {
        // Scene will enter foreground from background
    }
}

SwiftUI Equivalents

In SwiftUI, you handle scene lifecycle through the App struct and environment:

@main
struct MyApp: App {
    @Environment(\.scenePhase) var scenePhase
    
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .onChange(of: scenePhase) { _, phase in
            switch phase {
            case .active: handleActive()
            case .inactive: handleInactive()
            case .background: handleBackground()
            @unknown default: break
            }
        }
    }
    
    func handleActive() { /* scene became active */ }
    func handleInactive() { /* scene will resign active */ }
    func handleBackground() { /* scene entered background */ }
}

Scene Session

Each scene has a UISceneSession that holds persistent data for that scene. The system uses this to restore scenes after termination:

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options: UIScene.ConnectionOptions) {
    // session.stateRestorationActivity contains saved state
    if let activity = session.stateRestorationActivity {
        // Restore from activity
    }
}

In SwiftUI, WindowGroup handles state restoration automatically.

Multi-Window on iPad

Enabling Multi-Window

iPad apps support multiple windows by default when using WindowGroup. To explicitly control this:

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup("Main") {
            ContentView()
        }
    }
}

To open a new window, the system provides the newWindowScene action:

struct ContentView: View {
    @Environment(\.openWindow) var openWindow
    
    var body: some View {
        Button("Open Settings Window") {
            openWindow(id: "settings")
        }
    }
}

Define the window group in your App struct:

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup("Main") {
            ContentView()
        }
        WindowGroup(id: "settings") {
            SettingsView()
        }
    }
}

Scene Phases on iPad

Each scene has its own phase. One scene can be active while another is in the background:

struct ContentView: View {
    @Environment(\.scenePhase) var scenePhase
    
    var body: some View {
        Text("This scene is \(scenePhase == .active ? "active" : "not active")")
    }
}

Handling Scene Destruction

The system can destroy scenes to free resources. The scene phase transitions to .background before destruction:

.onChange(of: scenePhase) { _, phase in
    if phase == .background {
        // Save scene-specific state before potential destruction
        saveSceneState()
    }
}

Scene-Level Configuration

Each scene can have different configurations:

var body: some Scene {
    WindowGroup("Document") {
        DocumentEditor()
    }
    .defaultSize(width: 800, height: 600)
    .windowResizability(.contentSize)
    
    WindowGroup("Inspector") {
        InspectorView()
    }
    .defaultSize(width: 300, height: 400)
}

Universal Apps

For apps that run on both iPhone and iPad:

#if os(iOS)
// iOS-specific scene configuration
#elseif os(macOS)
// macOS-specific scene configuration
#endif

Multi-window is primarily an iPad feature. iPhone apps have a single scene managed by the system.

Scene Phases

Scene Phase Values

SwiftUI defines three scene phase values:

  • .active - The scene is in the foreground and receiving user interaction
  • .inactive - The scene is in the foreground but not receiving events (transitional state)
  • .background - The scene is in the background, not visible to the user

Responding to Phase Changes

@main
struct MyApp: App {
    @Environment(\.scenePhase) var scenePhase
    
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .onChange(of: scenePhase) { _, newPhase in
            switch newPhase {
            case .active:
                resumeTimers()
                refreshContent()
            case .inactive:
                pauseTimers()
                saveDraft()
            case .background:
                saveAllState()
                releaseHeavyResources()
            @unknown default:
                break
            }
        }
    }
}

Per-Scene Phase Observation

Each WindowGroup has its own scene phase. Multiple windows can be in different phases simultaneously:

struct MyScene: Scene {
    var body: some Scene {
        WindowGroup("Window 1") {
            ContentView()
        }
        .onChange(of: scenePhase) { _, phase in
            // Only affects Window 1
        }
    }
}

Practical Patterns

Auto-save on background:

.onChange(of: scenePhase) { _, phase in
    if phase == .background {
        modelContext.save()
    }
}

Resume animations when active:

@State private var isAnimating = false

.onChange(of: scenePhase) { _, phase in
    if phase == .active {
        withAnimation { isAnimating = true }
    } else {
        isAnimating = false
    }
}

Connect to services when active:

.onChange(of: scenePhase) { _, phase in
    if phase == .active {
        connectWebSocket()
    } else if phase == .background {
        disconnectWebSocket()
    }
}

Inactive State

The inactive state is brief and transitional. Common triggers include:

  • Incoming phone call
  • Siri activation
  • App switcher gesture
  • Notification banner overlay

Do not perform heavy work in the inactive state. Use it to pause ongoing operations that should not continue in the background.

Quiz

1. What is a UISceneSession?

Question 1 options

2. How do you enable multi-window on iPad in SwiftUI?

Question 2 options

3. What are the three scene phase values in SwiftUI?

Question 3 options

4. When does the inactive scene phase occur?

Question 4 options

5. How does SwiftUI handle state restoration for WindowGroup?

Question 5 options

Flashcards

Question

What does UISceneSession hold?

Answer

Persistent data for a scene, used for state restoration after termination.

Question

How do you open a new window on iPad?

Answer

Use @Environment(\.openWindow) and call openWindow(id:) with a defined WindowGroup id.

Question

What is the .inactive scene phase?

Answer

A brief transitional state when the scene is in the foreground but not receiving events.

Question

How do multiple WindowGroups behave on iPad?

Answer

Each WindowGroup creates a separate window that can be in a different scene phase.

Question

What triggers the .background scene phase?

Answer

When the scene is no longer visible, such as when the user switches to another app.

Revision Notes

Key Takeaways

  • 1. A Scene represents a single window with independent lifecycle
  • 2. UISceneSession stores persistent scene data for restoration
  • 3. Three scene phases: active, inactive, background
  • 4. Multiple WindowGroups enable multi-window on iPad
  • 5. WindowGroup handles state restoration automatically

Interview Tips

  • Explain the difference between scene lifecycle and app lifecycle
  • Know how to enable and manage multi-window on iPad
  • Understand when each scene phase occurs and how to respond
  • Be ready to discuss state restoration with WindowGroup

Cheat Sheet

Scene = one window instance with its own lifecycle.
UISceneSession holds persistent data for restoration.
Three phases: active, inactive, background.
Multi-window: multiple WindowGroups in App struct.
.openWindow(id:) opens new windows on iPad.