Swift 掌握 Observation 框架

前言

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 中的数据流管理更加轻松和高效。

相关推荐
茶底世界之下1 天前
时间线效果层,为什么不能只复用一个处理器?
ios·swift
Cc.Y1 天前
Swift深度链接的极简之道
ios·cocoa·swift
传奇开心果编程2 天前
【SwiftUI提高练中学】第9课 安全与隐私
swiftui·移动开发·swift·编程语言理论·开发范式·编程框架·声明式ui
CocoaKier2 天前
苹果商店详情顶部头图已面向所有开发者开放!
ios·apple
茶底世界之下2 天前
动态切换 nearest / linear 时,为什么不能只替换 sampler
ios·swift
应用市场3 天前
把旧 Pixel 变成相册备份中转站(上):Mac 到安卓的照片传输工具设计——流式上传、sha256 校验、adb forward 与 Bonjour
android·macos·adb·kotlin·swift
传奇开心果编程3 天前
【声明式UI开发实用技术学与练】第8课 状态提升与下放
学习·flutter·react native·ui·swiftui·composer
东坡肘子3 天前
Swift Server,又多了一个 Google -- 肘子的 Swift 周报 #156
人工智能·swiftui·swift
打工仔折腾 AI3 天前
从零写一个CAD 05:以鼠标为中心的滚轮缩放,招法能复用但顺序不能反
开发语言·后端·python·性能优化·计算机外设·swift·ai agent 实战
传奇开心果编程3 天前
【现代声明式UI学与练】第4课 列表渲染与 key——如何高效渲染列表、key 的作用、列表重排时的状态保持
学习·flutter·react native·ui·swiftui·android jetpack