App lifecycle and scene management

Sofia Martinez Jan 2026
2 tabs
import UIKit

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    var window: UIWindow?
    var appCoordinator: AppCoordinator?

    func scene(
        _ scene: UIScene,
        willConnectTo session: UISceneSession,
        options connectionOptions: UIScene.ConnectionOptions
    ) {
        guard let windowScene = scene as? UIWindowScene else { return }

        let window = UIWindow(windowScene: windowScene)
        self.window = window

        // Initialize app coordinator
        let coordinator = AppCoordinator(window: window)
        self.appCoordinator = coordinator
        coordinator.start()

        // Handle URL if app opened via deep link
        if let urlContext = connectionOptions.urlContexts.first {
            handleDeepLink(url: urlContext.url)
        }

        // Handle notification if app opened from notification
        if let notification = connectionOptions.notificationResponse {
            handleNotification(response: notification)
        }
    }

    func sceneDidDisconnect(_ scene: UIScene) {
        // Scene disconnected - release resources
        print("Scene disconnected")
    }

    func sceneDidBecomeActive(_ scene: UIScene) {
        // Scene became active - resume tasks, refresh UI
        print("Scene became active")
        NotificationCenter.default.post(name: .appDidBecomeActive, object: nil)
    }

    func sceneWillResignActive(_ scene: UIScene) {
        // Scene about to become inactive - pause tasks
        print("Scene will resign active")
    }

    func sceneWillEnterForeground(_ scene: UIScene) {
        // Scene entering foreground - refresh data
        print("Scene will enter foreground")
    }

    func sceneDidEnterBackground(_ scene: UIScene) {
        // Scene entered background - save data
        print("Scene did enter background")
        CoreDataStack.shared.saveContext()
    }

    func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
        guard let url = URLContexts.first?.url else { return }
        handleDeepLink(url: url)
    }

    private func handleDeepLink(url: URL) {
        // Parse deep link and navigate
        // Example: myapp://post/123
        if url.scheme == "myapp",
           url.host == "post",
           let postId = Int(url.lastPathComponent) {
            NotificationCenter.default.post(
                name: .navigateToPost,
                object: nil,
                userInfo: ["postId": postId]
            )
        }
    }

    private func handleNotification(response: UNNotificationResponse) {
        let userInfo = response.notification.request.content.userInfo
        if let postId = userInfo["post_id"] as? Int {
            NotificationCenter.default.post(
                name: .navigateToPost,
                object: nil,
                userInfo: ["postId": postId]
            )
        }
    }
}

extension Notification.Name {
    static let appDidBecomeActive = Notification.Name("appDidBecomeActive")
}
2 files · swift Explain with highlit

iOS 13 introduced scene-based lifecycle for multi-window support on iPad. The App and Scene delegates handle different lifecycle events. SceneDelegate manages individual scenes—windows on iPad or the single window on iPhone. It responds to state transitions like foreground, background, and disconnect. AppDelegate handles app-level events like launch and termination. For SwiftUI apps, I use @main with App protocol and scene modifiers like .onOpenURL() for deep links. Background tasks require specific capabilities and scheduling with BGTaskScheduler. Understanding the lifecycle prevents bugs around state restoration, data persistence, and resource management.