swift-app-lifecycle

Phase 0-1 (Scaffold/Architecture). Load when setting up app entry point, scenes, and system integration. Related: macos-development for macOS-specific architecture patterns.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "swift-app-lifecycle" with this command: npx skills add kmshdev/claude-swift-toolkit/kmshdev-claude-swift-toolkit-swift-app-lifecycle

Swift App Lifecycle

Lifecycle Position

Phase 0-1 (Scaffold/Architecture). Load when setting up app entry point, scenes, and system integration. Related: macos-development for macOS-specific architecture patterns.

App Entry Point

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

    #if os(macOS)
    Settings {
        SettingsView()
    }

    Window("About", id: "about") {
        AboutView()
    }

    MenuBarExtra("Status", systemImage: "circle.fill") {
        MenuBarView()
    }
    #endif
}

}

Scene Types

Scene Use Case Platform

WindowGroup

Main app window (supports multiple instances) All

Window

Single-instance utility window macOS

DocumentGroup

Document-based app (open/save/new) All

Settings

Preferences window (⌘,) macOS

MenuBarExtra

Menu bar icon with popover or window macOS

WindowGroup with Value (macOS multi-window)

WindowGroup(for: Project.ID.self) { $projectID in if let projectID { ProjectView(id: projectID) } }

// Open from anywhere: @Environment(.openWindow) private var openWindow Button("Open Project") { openWindow(value: project.id) }

ScenePhase Lifecycle

@Environment(.scenePhase) private var scenePhase

.onChange(of: scenePhase) { oldPhase, newPhase in switch newPhase { case .active: // App is visible and interactive refreshData() case .inactive: // App is visible but not interactive (e.g., notification shade) pauseTimers() case .background: // App is not visible — save state NOW saveState() scheduleBackgroundRefresh() @unknown default: break } }

State preservation pattern:

@main struct MyApp: App { @Environment(.scenePhase) private var scenePhase

var body: some Scene {
    WindowGroup {
        ContentView()
    }
    .onChange(of: scenePhase) { _, newPhase in
        if newPhase == .background {
            // For actor-based persistence, see swift-actor-persistence skill
            PersistenceController.shared.save()
        }
    }
}

}

Deep Links

Custom URL Schemes

// Info.plist: Add URL Types → URL Schemes: "myapp" // Handles: myapp://path/to/content?id=123

WindowGroup { ContentView() .onOpenURL { url in router.handle(url) } }

// Router pattern @Observable class DeepLinkRouter { var selectedTab: Tab = .home var presentedItem: Item?

func handle(_ url: URL) {
    guard url.scheme == "myapp" else { return }
    switch url.host {
    case "item":
        if let id = url.pathComponents.last {
            presentedItem = Item(id: id)
        }
    case "settings":
        selectedTab = .settings
    default: break
    }
}

}

Universal Links

// Requires Associated Domains entitlement: applinks:example.com // apple-app-site-association file on your server

.onOpenURL { url in // Handles both custom scheme AND https://example.com/item/123 router.handle(url) }

Push Notifications

Permission Flow

import UserNotifications

func requestNotificationPermission() async -> Bool { let center = UNUserNotificationCenter.current() do { let granted = try await center.requestAuthorization(options: [.alert, .badge, .sound]) if granted { await MainActor.run { UIApplication.shared.registerForRemoteNotifications() } } return granted } catch { return false } }

Registration

// In AppDelegate adaptor: class AppDelegate: NSObject, UIApplicationDelegate { func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { let token = deviceToken.map { String(format: "%02x", $0) }.joined() // Send token to your server }

func application(_ application: UIApplication,
                 didFailToRegisterForRemoteNotificationsWithError error: Error) {
    print("Push registration failed: \(error)")
}

}

// Connect in App @main struct MyApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate // ... }

Background Tasks

import BackgroundTasks

// Register in App init or AppDelegate BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.app.refresh", using: nil) { task in handleAppRefresh(task: task as! BGAppRefreshTask) }

// Schedule func scheduleBackgroundRefresh() { let request = BGAppRefreshTaskRequest(identifier: "com.app.refresh") request.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60) // 15 min try? BGTaskScheduler.shared.submit(request) }

// Handle func handleAppRefresh(task: BGAppRefreshTask) { scheduleBackgroundRefresh() // Schedule next one

let operation = Task {
    await refreshContent()
}

task.expirationHandler = { operation.cancel() }

Task {
    await operation.value
    task.setTaskCompleted(success: true)
}

}

Info.plist requirement:

<key>BGTaskSchedulerPermittedIdentifiers</key> <array> <string>com.app.refresh</string> </array>

macOS-Specific

NSApplicationDelegateAdaptor

class AppDelegate: NSObject, NSApplicationDelegate { func applicationDidFinishLaunching(_ notification: Notification) { } func applicationWillTerminate(_ notification: Notification) { // Final cleanup } func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { true // Quit when last window closes (document apps usually return false) } }

@main struct MyApp: App { @NSApplicationDelegateAdaptor(AppDelegate.self) var delegate // ... }

Menu Commands

var body: some Scene { WindowGroup { ContentView() } .commands { CommandGroup(replacing: .newItem) { Button("New Project") { /* ... / } .keyboardShortcut("n", modifiers: .command) } CommandMenu("Tools") { Button("Run Analysis") { / ... */ } .keyboardShortcut("r", modifiers: [.command, .shift]) } } }

Common Mistakes

  • Not saving state on .background — data lost when system terminates backgrounded app

  • Requesting notification permission on launch — ask in context when user understands the value

  • Missing @UIApplicationDelegateAdaptor / @NSApplicationDelegateAdaptor — needed for push tokens and system callbacks

  • Background tasks not registered in Info.plist — silently fails to schedule

  • applicationShouldTerminateAfterLastWindowClosed not set on macOS — app stays running with no windows

Checklist

  • State saved on .background scene phase transition

  • Deep link routes cover all app entry points

  • Notification permissions requested contextually (not on first launch)

  • Background tasks registered in Info.plist with matching identifiers

  • macOS apps handle applicationShouldTerminateAfterLastWindowClosed

  • Universal Links have apple-app-site-association file on server

  • @main App struct is the single entry point (no duplicate @main )

Templates

App lifecycle helpers in templates/ — copy and adapt:

  • ForceUpdateChecker.swift — Version comparison with remote config, hard-block vs soft-prompt UI, App Store redirect

  • StateRestorationManager.swift — @MainActor @Observable state restoration with NavigationPath persistence, debounced save, restore-behavior policies

Cross-References

  • macos-development — macOS architecture patterns, SwiftData setup, AppKit bridging

  • swift-concurrency — async/await in app delegate methods and background tasks

  • swift-networking — background transfer configuration for downloads

  • app-development-workflow — lifecycle Phase 0-1 context

  • swift-actor-persistence — actor-based repositories for implementing save() on .background scene phase

  • swiftui-26-api — UIHostingSceneDelegate for bridging UIKit scene lifecycle to SwiftUI

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

swiftui-26-api

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

swift-concurrency

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

swift-actor-persistence

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

swiftui-components

No summary provided by upstream source.

Repository SourceNeeds Review