使用 Observation 框架 App 管理全局状态

了解如何使用最新的 Observation 框架,简化通过 AppState 管理 App 状态的流程。

使用 Observation 框架 App 管理全局状态

在之前的这篇文章中,我们详细讲解了如何使用 EnvironmentObject 创建 AppState,来管理全局状态:

使用 EnvironmentObject 和 AppState 管理全局状态
在应用程序开发中,组件之间的状态传递是一个常见的需求。管理状态的方式会影响代码的可维护性、可扩展性以及可读性。本文将介绍两种常见的状态传递方式:变量传递和全局状态管理。 使用变量传递状态 变量传递是最直观的状态传递方式。在这种方式中,我们在声明组件时创建变量,并在需要的地方通过显式方式传递这些变量。 举个例子,假设我们有一个父组件 ParentView 和一个子组件 ChildView,我们可以通过变量传递在这两个组件之间共享数据。 struct ParentView: View { @State private var username: String = “John Doe” var body: some View { VStack { Text(“Parent View”) ChildView(username: $username) Text(“Username: \(username)”) } } } struct ChildView:

从 iOS 17 开始,Swift 推出了全新的 Observation 框架。通过 Observation 框架创建 AppState,可以更高效地管理全局状态,让代码更清晰简洁,进一步提升开发体验和可读性。

使用 Observation 创建 AppState

Observation 框架提供了一种新的方式来观察和响应状态变化。

定义 AppState 类

在使用 Observation 框架时,你只需要让你的类遵循 Observable 协议,而不需要对属性进行额外的标记

import SwiftUI
import Observation

@Observable
class AppState {
    var isAuthenticated: Bool = false

    func logIn() {
        // 这里可以添加实际的登录逻辑
        self.isAuthenticated = true
    }

    func logOut() {
        // 这里可以添加实际的登出逻辑
        self.isAuthenticated = false
    }
}

在 App 文件中初始化 AppState 实例

在应用的入口点(通常是 App 的主体部分),创建 AppState 的实例,并使用 .environment 方法将它注入到环境中。

和 EnvironmentObject 不同,我们无需使用 @StateObject 属性包装器,使用基本的@State 即可。

@main
struct MyApp: App {
    @State var appState = AppState()

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(appState)
        }
    }
}

注意是 var appState = AppState(),而不是 var appState: AppState,前者创建了一个 AppState 类的实例,而后者没有。

在视图中访问 AppState

在 SwiftUI 视图中,可以通过 @Environment 属性包装器来获取 AppState 的实例。当状态更新时,SwiftUI 组件会自动更新 UI。

struct ContentView: View {
    @Environment(AppState.self) private var appState

    var body: some View {
        VStack {
            if appState.isAuthenticated {
                Text("用户已认证")
                Button("登出") {
                    appState.logOut()
                }
            } else {
                Text("用户未认证")
                Button("登录") {
                    appState.logIn()
                }
            }
        }
    }
}

这样,我们就实现了一个简单的登录逻辑,将这些逻辑放在 AppState 中,可以让任何视图通过简单地调用 appState.logIn() 来实现用户登录,而无需关心登录过程的具体细节。这种方法也便于在后续需要修改登录逻辑时,只需在 AppState 中修改,无需触及任何使用了登录功能的视图。

在双向绑定组件中使用 AppState

要在支持双向绑定的 SwiftUI 组件中使用 AppState,可以使用 @Bindable 属性包装器。

例如,将 appState 绑定到 Picker 组件上:

struct HomeView: View 
    @Environment(AppState.self) private var appState

    var body: some View {
        NavigationStack {
            HomeContentView(
            )
            .safeAreaInset(edge: .top, spacing: 0) {
                // 在这里使用 @Bindable
                @Bindable var appState = appState
                Picker("记录类型", selection: $appState.selected) {
                    ForEach([Category.all, .income, .expense]) { category in
                        Text(category.displayName)
                            .tag(category)
                    }
                }
                .pickerStyle(.segmented)
            }
        }
    }
}