Dripper 是一个用于 SwiftUI 项目的架构框架。
它是一个轻量级框架,专注于 Point-Free 的 Swift-Composable-Architecture 的核心概念。
以下是我们需要的核心概念
我们希望尽可能多地使用原生 Swift 功能,因此我们决定使用 @Observable
而不是像 TCA 中 @ObservableState
那样的自定义观察机制。
遗憾的是,由于 @Observable
的限制,这意味着我们无法使用可靠的基于结构体的状态管理。
@Observable
目前仅支持基于类的属性,因此我们不得不为我们的 State
使用类。
一旦 Swift 支持基于类的属性,我们将考虑迁移到基于结构体(或基于 Actor)的状态管理。
它基本上类似于原始的 TCA,但进行了一些简化。
这是一个简单的例子
首先,我们必须创建一个符合 Dripper
协议的 Dripper
结构体。
它的作用等同于 TCA 中的 Reducer
。
import Dripper
struct Counter: Dripper {
@Observable
final class State: @unchecked Sendable {
var count = 0
@ObservationIgnored private let id: UUID
init(count: Int = .zero) {
self.count = count
self.id = UUID()
}
}
enum Action {
case increase
case decrease
}
var body: some Dripper<State, Action> {
Drip { state, action in
switch action {
case .increase:
state.count += 1
return .none
case .decrease:
state.count -= 1
return .none
}
}
}
}
注意
您需要将 @unchecked Sendable
添加到 State
类以抑制编译器错误。虽然 State
本身实际上不是线程安全的,但在 Station
中使用时,由于它由 StateHandler
actor 管理,因此保证是线程安全的。
我们将在未来的更新中为此实现更好的解决方案。此外,欢迎随时就此问题提出任何改进建议! 😊
要在您的 SwiftUI 视图中使用 Dripper
,请创建一个以 Dripper
作为其泛型类型参数的 Station
实例。
import SwiftUI
import Dripper
struct ContentView: View {
let station: StationOf<Counter>
}
#Preview {
CounterView(
station: Station(initialState: Counter.State()) {
Counter()
}
Button("\(station.count)") {
station.pour(.increase)
}
)
}
您可以使用 pour
方法触发 Action
,并通过 Station
属性直接访问状态。
Effect
帮助您处理副作用,例如异步操作。
还记得我们在 Dripper
示例中看到的 .none
吗?
实际上,那是 Effect
之一,表示不会发生副作用。
这是一个关于如何使用 Effect
的示例
import Dripper
var body: some Dripper<State, Action> {
Drip { state, action in
switch action {
case .increase:
state.count += 1
return .none // means no side-effect
case .decrease:
state.count -= 1
return .run { pour in // means there's a side-effect
let score = try await fetchScore(for: .now)
let action = score.isPositive ? Action.increase : Action.decrease
pour(action) // you can trigger another action
}
}
}
}
要处理副作用,请将 .run
与接收 pour
函数的闭包一起使用。
在此闭包内,您可以通过使用所需的 Action
作为参数调用 pour
来触发其他操作。
感谢您查看 Dripper! 欢迎提出问题和贡献 😊
MIT 许可证 - LICENSE