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

相关推荐
Swift社区6 小时前
Excel 列名称转换问题 Swift 解答
开发语言·excel·swift
东坡肘子1 天前
肘子的 Swift 周报 #063|异种肾脏移植取得突破
swiftui·swift·apple
恋猫de小郭2 天前
什么?Flutter 可能会被 SwiftUI/ArkUI 化?全新的 Flutter Roadmap
flutter·ios·swiftui
威化饼的一隅2 天前
【多模态】swift-3框架使用
人工智能·深度学习·大模型·swift·多模态
靴子学长3 天前
iOS + watchOS Tourism App(含源码可简单复现)
mysql·ios·swiftui
opentogether4 天前
Swift 的动态性
开发语言·ssh·swift
苍墨穹天4 天前
SWIFT基本使用
linux·swift
SchneeDuan5 天前
从源码分析swift GCD_DispatchGroup
ios·swift·源码分析·gcd
请叫我飞哥@6 天前
iOS在项目中设置 Dev、Staging 和 Prod 三个不同的环境
ios·xcode·swift
Cedric_Anik9 天前
iOS渲染概述
ui·ios·swift