App essentials in SwiftUI
Swift & UI 入门 26m

SwiftUI 中的 App 基础

App essentials in SwiftUI

2020年6月22日

在 Apple 官方观看视频

一句话判断

SwiftUI 终于有了自己的 App 协议——不再需要 UIKit 的 AppDelegate/SceneDelegate 样板代码,一个纯 SwiftUI App 可以从不到 10 行代码开始,这是 SwiftUI 从”UI 框架”升级为”全栈框架”的标志。

这场 Session 讲了什么

iOS 14 引入了 App 协议和 Scene 协议,让 SwiftUI App 可以完全脱离 UIKit 的生命周期管理。过去即使是 SwiftUI 项目也需要 AppDelegate 和 SceneDelegate 来处理启动、前后台切换等事件,现在这些都可以在 SwiftUI 中声明式地完成。

核心 API:@main 标注的 App 结构体是整个 App 的入口,body 属性返回 Scene,Scene 内包含 ViewWindowGroup 是最常用的 Scene 类型,自动处理多窗口(iPadOS)、状态恢复等系统行为。

Session 还介绍了 @UIApplicationDelegateAdaptor(在 SwiftUI 中桥接 AppDelegate),@Environment 的新值(如 \.scenePhase 用于监听前后台切换),以及 Settings Scene(macOS 上的偏好设置窗口)。

值得深挖的点

从命令式生命周期到声明式。 过去你在 AppDelegate 的 didFinishLaunching 中配置全局状态,在 didEnterBackground 中保存数据。SwiftUI 的方式是在 body 中声明 App 的 Scene 结构,在 View 中用 @Environment(\.scenePhase) 响应状态变化。这种转变把”做什么”和”什么时候做”解耦了。

WindowGroup 的多窗口支持。 在 iPadOS 上,WindowGroup 自动支持创建多个窗口实例。每个窗口有独立的状态(因为 View 是 struct),数据可以通过 @StateObject@EnvironmentObject 在窗口之间共享。系统还自动处理窗口的状态保存和恢复。

代码片段

import SwiftUI

// 最简单的 SwiftUI App——不到 10 行代码
@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}
// 监听 App 前后台状态变化
struct ContentView: View {
    @Environment(\.scenePhase) var scenePhase
    
    var body: some View {
        NavigationView {
            Text("Hello, World!")
        }
        .onChange(of: scenePhase) { newPhase in
            switch newPhase {
            case .active:
                print("App 进入前台并活跃")
            case .inactive:
                print("App 进入前台但不活跃(如来电)")
            case .background:
                print("App 进入后台")
                saveUserData()  // 在后台时保存数据
            @unknown default:
                break
            }
        }
    }
}
// 完整的 App 示例:包含多个 Scene
@main
struct MyApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    @StateObject var dataController = DataController()
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(dataController)
        }
        
        // macOS 上的设置窗口
        #if os(macOS)
        Settings {
            SettingsView()
                .environmentObject(dataController)
        }
        #endif
    }
}

// 如果需要处理推送通知等,仍然可以桥接 AppDelegate
class AppDelegate: NSObject, UIApplicationDelegate {
    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // 配置推送通知等
        return true
    }
}

最佳实践

  • 新项目直接用 SwiftUI App 协议。 不需要 AppDelegate 除非你有推送通知、URL Scheme 等必须用 AppDelegate 处理的需求。
  • @StateObject 创建全局数据管理器。 在 App 级别创建的 @StateObject 可以通过 .environmentObject 传递给所有子视图。
  • scenePhase 替代 AppDelegate 的前后台回调。 逻辑更清晰,且可以在任何 View 中使用。
  • macOS 项目用 Settings Scene。 不需要手动创建设置窗口,SwiftUI 自动管理。

还有什么值得关注

  • DocumentGroup 可以用来创建文档类 App(如编辑器),自动处理文件打开/保存
  • WindowGroupid 参数可以区分不同类型的窗口
  • SwiftUI App 的生命周期在 watchOS 和 tvOS 上有略微不同的行为
  • @UIApplicationDelegateAdaptor 支持 ObservableObject,可以与 SwiftUI 数据流结合
WWDC 2020