Swift 掌握 Observation 框架

2023-12-21 19:12:22 浏览数 (2)

前言

Apple 推出了由 Swift 语言的宏功能支持的新观察框架。新的观察框架与 Swift 并发功能结合使用,允许我们替代 Apple 公司看起来已经过时的 Combine 框架。下面将介绍如何使用观察框架来处理应用程序中的数据流。

使用 @Observable

RevenueCat 简化了实施应用内购买、管理客户和扩展应用业务的过程。无论你是第一次添加应用内购买和订阅,还是已经有数百万付费用户,你都可以在几分钟内开始使用 RevenueCat。看看为什么有超过 30,000 个应用程序使用 RevenueCat 来支持其应用业务。你可以查看他们的文档以了解更多信息。

使用新的观察框架非常容易。你只需要使用 @Observable 宏标记你的类。

代码语言:swift复制
@Observable final class Store<State, Action> {

    typealias Reduce = (State, Action) -> State

    

    private(set) var state: State

    private let reduce: Reduce

    

    init(initialState state: State, reduce: @escaping Reduce) {

        self.state = state

        self.reduce = reduce

    }

    

    func send(_ action: Action) {

        state = reduce(state, action)

    }

}

观察 Store

正如你在上面的示例中所看到的,我们使用 @Observable 宏来注释我们的 Store 类型。之后,我们可以观察 Store 类型中的任何变量。我们在 Store 类型中只有一个变量,用于定义存储的状态。另一个字段是一个永不更改的 let 常量。

代码语言:swift复制
withObservationTracking {

    render(store.state)

} onChange: {

    print("State changed")

}

调用闭包

要观察 Store 类型的实例,我们需要使用 withObservationTracking 函数调用两个闭包。在第一个闭包中,我们可以访问可观察类型的所有必要属性。观察框架仅在触摸到的观察类型的任何属性更改后才调用第二个闭包。

代码语言:swift复制
func startObservation() {

    withObservationTracking {

        render(store.state)

    } onChange: {

        Task { startObservation() }

    }

}

观察框架仅运行一次 onChange,这意味着你应该递归调用它以不断观察更改。你还应该注意的另一件事是 onChange 闭包在实际更改应用之前运行。这就是为什么我们通过启动新任务来推迟 onChange 操作的原因。

SwiftUI 自动跟踪

在 SwiftUI 中,你不需要使用 withObservationTracking 函数来观察更改。SwiftUI 自动跟踪视图正文中使用的任何可观察类型属性的更改。

代码语言:swift复制
struct ProductsView: View {

    let store: Store<AppState, AppAction>

    

    var body: some View {

        List(store.state.products, id: .self) { product in

            Text(product)

        }

        .onAppear {

            store.send(.fetch)

        }

    }

}

正如你在上面的示例中所看到的,我们不使用任何属性包装器来观察存储。SwiftUI 自动执行此操作。只要存储的状态属性更改,SwiftUI 就会更新视图。我们不需要 @ObservedObject 属性包装器来跟踪可观察类型中的更改,但我们仍然需要 @StateObject 替代项以在 SwiftUI 生命周期中存活。

使用 @State

Apple 简化了我们应该在新的观察框架中使用的属性包装器集。现在,我们可以使用 @State 而不是 @StateObject 属性包装器。@State 属性包装器现在适用于简单的值类型和任何可观察类型。

代码语言:swift复制
struct ContentView: View {

    @State private var store = Store<AppState, AppAction>(

        initialState: .init(),

        reduce: reduce

    )

    

    var body: some View {

        ProductsView(store: store)

    }

}

使用 @Environment

相同的方法适用于 SwiftUI 框架的环境功能。现在不再需要 @EnvironmentObject 属性包装器。你现在可以使用 @Environment 属性包装器和具有可观察类型的环境视图修改器。

代码语言:swift复制
struct ContentView: View {

    @State private var store = Store<AppState, AppAction>(

        initialState: .init(),

        reduce: reduce

    )

    

    var body: some View {

        ProductsView()

            .environment(store)

    }

}



struct ProductsView: View {

    @Environment(Store<AppState, AppAction>.self) var store

    

    var body: some View {

        List(store.state.products, id: .self) { product in

            Text(product)

        }

        .onAppear {

            store.send(.fetch)

        }

    }

}

使用 @Bindable

你可能会想知道的最后一件事是如何从可观察类型中派生绑定。SwiftUI 为此引入了 @Bindable 属性包装器,只能与可观察类型一起使用。

代码语言:swift复制
@Observable final class AuthViewModel {

    var username = ""

    var password = ""

    

    var isAuthorized = false

    

    func authorize() {

        isAuthorized.toggle()

    }

}



struct AuthView: View {

    @Bindable var viewModel: AuthViewModel

    

    var body: some View {

        VStack {

            if !viewModel.isAuthorized {

                TextField("username", text: $viewModel.username)

                SecureField("password", text: $viewModel.password)

                

                Button("authorize") {

                    viewModel.authorize()

                }

            } else {

                Text("Hello, (viewModel.username)")

            }

        }

    }

}

你可以使用 @Bindable 属性包装器轻松地从任何可观察类型的属性创建绑定。有时,你可能需要内联 @Bindable 到视图正文中以创建绑定。

代码语言:swift复制
struct InlineAuthView: View {

    @Environment(AuthViewModel.self) var viewModel

    

    var body: some View {

        @Bindable var viewModel = viewModel

        

        VStack {

            if !viewModel.isAuthorized {

                TextField("username", text: $viewModel.username)

                SecureField("password", text: $viewModel.password)

                

                Button("authorize") {

                    viewModel.authorize()

                }

            } else {

                Text("Hello, (viewModel.username)")

            }

        }

    }

}

总结

这篇文章介绍了苹果引入的全新观察框架,该框架利用 Swift 语言的宏功能。新的观察框架结合了 Swift 并发功能,使我们能够替代苹果看似已经过时的 Combine 框架。

总的来说,新的观察框架使 SwiftUI 中的数据流管理更加轻松和高效。

0 人点赞