此源码解析适用于
0.41.0
之前的版本。最新的源码已经把Reducer
重构成了一个协议ReducerProtocol
,但核心的实现方法还是离不开这个版本的源码。
ViewStore
可以监听 state 的变化,并且发送 action。通常用于试图,例如 SwiftUI views,UIView
和 UIViewController
。
在 SwiftUI 应用中,ViewStore
通常可以在 WithViewStore
中访问。例如:
var body: some View {
WithViewStore(self.store, observe: { $0 }) { viewStore in
VStack {
Text(“Current count: \(viewStore.count)”)
Button(“Increment”) { viewStore.send(.incrementButtonTapped) }
}
}
}
ViewStore
还可以被 @ObservedObject
标记:
@ObservedObject var viewStore: ViewStore<State, Action>
在 UIKit 中,ViewStore
可以使用 Store
来创建,然后订阅 state 的变化:
let store: Store<State, Action>
let viewStore: ViewStore<State, Action>
private var cancellables: Set<AnyCancellable> = []
init(store: Store<State, Action>) {
self.store = store
self.viewStore = ViewStore(store)
}
func viewDidLoad() {
super.viewDidLoad()
self.viewStore.publisher.count
.sink { [weak self] in self?.countLabel.text = $0 }
.store(in: &self.cancellables)
}
@objc func incrementButtonTapped() {
self.viewStore.send(.incrementButtonTapped)
}
注意:
ViewStore
类不是线程安全的,与它(以及从Store
派生自的)的所有交互必须发生在同一线程上。对于 SwiftUI 应用,所有交互都必须发生在主线程上。
@dynamicMemberLookup
public final class ViewStore<State, Action>: ObservableObject {
// 为了支持 iOS 13,而不是用 `@Published`,显式地声明 `objectWillChange`
public private(set) lazy var objectWillChange = ObservableObjectPublisher()
// 发送 action 的闭包
private let _send: (Action) -> Task<Void, Never>?
// 当前的 state
fileprivate let _state: CurrentValueRelay<State>
// 保存对 `store.state` 的订阅
private var viewCancellable: AnyCancellable?
public init(
_ store: Store<State, Action>,
removeDuplicates isDuplicate: @escaping (State, State) -> Bool
) {
// 直接调用 `store` 的 `send` 方法
self._send = { store.send($0) }
// 初始化 state
self._state = CurrentValueRelay(store.state.value)
// 订阅 `store.state` 的变化,并更新自己的 `_state`
self.viewCancellable = store.state
.removeDuplicates(by: isDuplicate)
.sink { [weak objectWillChange = self.objectWillChange, weak _state = self._state] in
guard let objectWillChange = objectWillChange, let _state = _state else { return }
objectWillChange.send()
_state.value = $0
}
}
internal init(_ viewStore: ViewStore<State, Action>) {
self._send = viewStore._send
self._state = viewStore._state
self.objectWillChange = viewStore.objectWillChange
self.viewCancellable = viewStore.viewCancellable
}
// 一个会发出 `State` 的 Publisher。支持动态成员查找(dynamic member lookup)。例如:
//
// ```swift
// viewStore.publisher.alert
// .sink { ... }
// ```
//
// 当这个 Publisher 发出值时,`ViewStore` 的 state 已经更新,所以下面的判断是可以通过的:
//
// ```swift
// viewStore.publisher
// .sink { precondition($0 == viewStore.state) }
// ```
//
// 这意味着可以使用闭包中的 state 或者直接使用 `viewStore.state`。
//
// 要注意的是,在一个 Publisher 上调用 `.sink` 的顺序并不意味着是闭包执行的顺序。
// 所以每个闭包执行的代码必须是相互独立的。
public var publisher: StorePublisher<State> {
// 具体实现请查看 `StorePublisher`
StorePublisher(viewStore: self)
}
// 当前的 state
public var state: State {
self._state.value
}
// 根据传入的 key path 返回对应的 state
public subscript<Value>(dynamicMember keyPath: KeyPath<State, Value>) -> Value {
self._state.value[keyPath: keyPath]
}
// 发送 action 到 store。
// 返回值是 `ViewStoreTask`,可以用它来控制 Effect 的生命周期。例如 SwiftUI 的 `task` modifier:
// ```swift
// .task { await viewStore.send(.task).finish() }
// ```
@discardableResult
public func send(_ action: Action) -> ViewStoreTask {
.init(rawValue: self._send(action))
}
// 以指定的动画发送 action 到 store
@discardableResult
public func send(_ action: Action, animation: Animation?) -> ViewStoreTask {
withAnimation(animation) {
self.send(action)
}
}
/// 发送 action 到 store,然后在某个 state 为 `true` 时挂起。
/// 此方法可用于与 async/await 代码交互,可以在 Effect 执行某些工作期间挂起。
/// 一个常见的例子是 SwiftUI 的 `.refreshable` 方法,它在执行工作时在屏幕上显示加载指示器。
///
/// 例如,列表的下拉刷新,我们可以这样写:
///
/// ```swift
/// struct State: Equatable {
/// var isLoading = false
/// var response: String?
/// }
///
/// enum Action {
/// case pulledToRefresh
/// case receivedResponse(TaskResult<String>)
/// }
///
/// struct Environment {
/// var fetch: () async throws -> String
/// }
///
/// let reducer = Reducer<State, Action, Environment> { state, action, environment in
/// switch action {
/// case .pulledToRefresh:
/// state.isLoading = true
/// return .task {
/// await .receivedResponse(TaskResult { try await environment.fetch() })
/// }
///
/// case let .receivedResponse(result):
/// state.isLoading = false
/// state.response = try? result.value
/// return .none
/// }
/// }
/// ```
///
/// 使用了 `isLoading` 来表示请求是否正在执行。 View 如下:
///
/// ```swift
/// struct MyView: View {
/// let store: Store<State, Action>
///
/// var body: some View {
/// WithViewStore(self.store, observe: { $0 }) { viewStore in
/// List {
/// if let response = viewStore.response {
/// Text(response)
/// }
/// }
/// .refreshable {
/// await viewStore.send(.pulledToRefresh, while: \.isLoading)
/// }
/// }
/// }
/// }
/// ```
///
/// 这里我们使用了 `send(_:while:)`,当 `isLoading` 为 `true` 时挂起。
/// 一旦它变成 `false` 将会继续执行, 会告诉 `.refreshable` 工作已经完成,加载指示器消失。
@MainActor
public func send(_ action: Action, while predicate: @escaping (State) -> Bool) async {
let task = self.send(action)
await withTaskCancellationHandler {
task.rawValue?.cancel()
} operation: {
// 具体实现请查看此方法
await self.yield(while: predicate)
}
}
/// 与上一个方法类似,不同的是此方法可以以指定动画执行 action
@MainActor
public func send(
_ action: Action,
animation: Animation?,
while predicate: @escaping (State) -> Bool
) async {
let task = withAnimation(animation) { self.send(action) }
await withTaskCancellationHandler {
task.rawValue?.cancel()
} operation: {
await self.yield(while: predicate)
}
}
/// 当 `predicate ` 为 `true` 时挂起当前任务
@MainActor
public func yield(while predicate: @escaping (State) -> Bool) async {
// 实现思路:订阅 `self.publisher`,等待它发出第一个值。
// 如果 `predicate` 为 true,值就不会发出,代码会停在这里
if #available(iOS 15, macOS 12, tvOS 15, watchOS 8, *) {
// 使用 await 等待 `self.publisher` 发出第一个值
_ = await self.publisher
.values
.first(where: { !predicate($0) })
} else {
let cancellable = Box<AnyCancellable?>(wrappedValue: nil)
try? await withTaskCancellationHandler {
cancellable.wrappedValue?.cancel()
} operation: {
// 检查 task 是否已经取消
try Task.checkCancellation()
// 使用 await 等待 continuation 完成
try await withUnsafeThrowingContinuation {
(continuation: UnsafeContinuation<Void, Error>) in
guard !Task.isCancelled else {
continuation.resume(throwing: CancellationError())
return
}
cancellable.wrappedValue = self.publisher
.filter { !predicate($0) }
.prefix(1)
.sink { _ in
// 得到了第一个值,continuation 继续
continuation.resume()
// 这里目的是防止 cancellable 被提前释放
_ = cancellable
}
}
}
}
}
/// 从 Store 派生一个 `Binding`,它可以防止直接对 State 进行修改,而是发送 Action 给 Store。
/// 非常适合用于 SwiftUI 的支持双向绑定的组件。例如 `TextField`:
///
/// ```swift
/// struct State { var name = "" }
/// enum Action { case nameChanged(String) }
///
/// TextField(
/// "Enter name",
/// text: viewStore.binding(
/// get: { $0.name },
/// send: { Action.nameChanged($0) }
/// )
/// )
/// ```
public func binding<Value>(
get: @escaping (State) -> Value,
send valueToAction: @escaping (Value) -> Action
) -> Binding<Value> {
ObservedObject(wrappedValue: self)
.projectedValue[get: .init(rawValue: get), send: .init(rawValue: valueToAction)]
}
/// `binding` 方法的一个变体。
/// alert binding 的例子:
///
/// ```swift
/// struct State { var alert: String? }
/// enum Action { case alertDismissed }
///
/// .alert(
/// item: self.store.binding(
/// get: { $0.alert },
/// send: .alertDismissed
/// )
/// ) { alert in Alert(title: Text(alert.message)) }
/// ```
public func binding<Value>(
get: @escaping (State) -> Value,
send action: Action
) -> Binding<Value> {
self.binding(get: get, send: { _ in action })
}
/// `binding` 方法的一个变体。
/// `TextField` 的例子:
///
/// ```swift
/// typealias State = String
/// enum Action { case nameChanged(String) }
///
/// TextField(
/// "Enter name",
/// text: viewStore.binding(
/// send: { Action.nameChanged($0) }
/// )
/// )
/// ```
public func binding(
send valueToAction: @escaping (State) -> Action
) -> Binding<State> {
self.binding(get: { $0 }, send: valueToAction)
}
/// `binding` 方法的一个变体。
/// alert binding 的例子:
///
/// ```swift
/// typealias State = String
/// enum Action { case alertDismissed }
///
/// .alert(
/// item: viewStore.binding(
/// send: .alertDismissed
/// )
/// ) { title in Alert(title: Text(title)) }
/// ```
public func binding(send action: Action) -> Binding<State> {
self.binding(send: { _ in action })
}
private subscript<Value>(
get state: HashableWrapper<(State) -> Value>,
send action: HashableWrapper<(Value) -> Action>
) -> Value {
get { state.rawValue(self.state) }
set { self.send(action.rawValue(newValue)) }
}
}
extension ViewStore where State: Equatable {
public convenience init(_ store: Store<State, Action>) {
self.init(store, removeDuplicates: ==)
}
}
extension ViewStore where State == Void {
public convenience init(_ store: Store<Void, Action>) {
self.init(store, removeDuplicates: ==)
}
}
/// `send` 方法返回的类型,代表着一个 `Effect` 的生命周期。
// 可以使用此值将 `Effect` 的生命周期关联到异步上下文中,如 `task` modifier:
///
/// ```swift
/// .task { await viewStore.send(.task).finish() }
/// ```
public struct ViewStoreTask: Hashable, Sendable {
fileprivate let rawValue: Task<Void, Never>?
// 取消底层的 task,并且等待它完成
public func cancel() async {
self.rawValue?.cancel()
await self.finish()
}
// 等待 task 完成
public func finish() async {
await self.rawValue?.cancellableValue
}
/// task 是否已取消。一旦变成 `true`,就无法再重新执行
public var isCancelled: Bool {
self.rawValue?.isCancelled ?? true
}
}
// State 的 Publisher
@dynamicMemberLookup
public struct StorePublisher<State>: Publisher {
public typealias Output = State
public typealias Failure = Never
public let upstream: AnyPublisher<State, Never>
public let viewStore: Any
fileprivate init<Action>(viewStore: ViewStore<State, Action>) {
self.viewStore = viewStore
// 上游就是 state
self.upstream = viewStore._state.eraseToAnyPublisher()
}
public func receive<S: Subscriber>(subscriber: S) where S.Input == Output, S.Failure == Failure {
// 接收到 `subscriber`,直接对 `upstream` 进行订阅
self.upstream.subscribe(
AnySubscriber(
receiveSubscription: subscriber.receive(subscription:),
receiveValue: subscriber.receive(_:),
receiveCompletion: { [viewStore = self.viewStore] in
subscriber.receive(completion: $0)
_ = viewStore
}
)
)
}
private init<P: Publisher>(
upstream: P,
viewStore: Any
) where P.Output == Output, P.Failure == Failure {
self.upstream = upstream.eraseToAnyPublisher()
self.viewStore = viewStore
}
/// 根据给定的 key path 返回某个 State 的 Publisher
public subscript<Value: Equatable>(
dynamicMember keyPath: KeyPath<State, Value>
) -> StorePublisher<Value> {
.init(upstream: self.upstream.map(keyPath).removeDuplicates(), viewStore: self.viewStore)
}
}
private struct HashableWrapper<Value>: Hashable {
let rawValue: Value
static func == (lhs: Self, rhs: Self) -> Bool { false }
func hash(into hasher: inout Hasher) {}
}