class Store {
// 觀察者,用于響應(yīng)狀態(tài)更新,第一個 State? 為舊狀態(tài),第二個 State 為當前狀態(tài)
typealias Observer = (State?, State) -> Void
private(set) var state: State // 當前狀態(tài)
private var _observers: [UUID: Observer] // 所有的觀察者
// 初始化
init(initailState: State) {
self.state = initailState
self._observers = [:]
}
// 發(fā)出事件
func dispatch(event: State.Event) {
let oldState = self.state
self.state = State.reduce(self.state, event: event)
_publish(oldState: oldState, newState: self.state)
}
// 訂閱狀態(tài)更新
func subscribe(observer: @escaping Observer) -> UUID {
let subscriptionID = UUID() // UUID 是唯一標識符,該 id 可用于取消訂閱
_observers[subscriptionID] = observer
observer(nil, self.state) // 訂閱時,將當前狀態(tài)回放給該觀察者
return subscriptionID
}
// 取消訂閱
func unsubscribe(_ subscriptionID: UUID) {
_observers.removeValue(forKey: subscriptionID)
}
// 私有方法,通知所有的觀察者,狀態(tài)已經(jīng)更新了
private func _publish(oldState: State?, newState: State) {
_observers.values.forEach { observer in
observer(oldState, newState)
}
}
}
如何使用 Store:
func useStore() {
//首先下創(chuàng)建 Store
let initailState = State(username: "",password: "",loading: false,data: nil,error: nil)
let store = Store(initailState: initailState)
// 然后,訂閱程序狀態(tài),并且將這些狀態(tài)錄制下來
// 以下變量 newStates 和 oldStates 用于錄制狀態(tài)歷史
var newStates: [State] = []
var oldStates: [State?] = []
let subscriptionID = store.subscribe { (oldState, newState) in
newStates.append(newState)
oldStates.append(oldState)
}
// 然后,模擬輸入用戶名事件和輸入密碼事件
// 模擬真實事件
store.dispatch(event: .onUpateUsername("beeth0ven"))
store.dispatch(event: .onUpatePassword("123456"))
// 取消訂閱
store.unsubscribe(subscriptionID)
// 最后,比對錄制的狀態(tài)是否符合預(yù)期
// 描敘預(yù)期
let expectNewStates = [
State(username: "",password: "",loading: false,data: nil,error: nil),
State(username: "beeth0ven",password: "",loading: false,data: nil,error: nil),
State(username: "beeth0ven",password: "123456",loading: false,data: nil,error: nil)
]
let expectOldStates = [
nil,
State(username: "",password: "",loading: false,data: nil,error: nil),
State(username: "beeth0ven",password: "",loading: false,data: nil,error: nil)
]
// 比對結(jié)果
newStates == expectNewStates // 結(jié)果:true ??
oldStates == expectOldStates // 結(jié)果:true ??
}
這就是如何在測試環(huán)境里面使用 Store,那么在 App 里面如何使用 Store 呢。 一個 相對簡單(并未優(yōu)化) 的方法,就是將 Store 注入到對應(yīng)的組件里面,這里以 ViewController 為例:
-
ViewController可以使用store.subscribe方法訂閱程序的狀態(tài)。當狀態(tài)更新時,比對新舊狀態(tài),然后刷新過時了的 UI。 - 當用戶觸發(fā)某個事件時,調(diào)用
store.dispatch方法將事件發(fā)出去,如:當用戶點擊登錄按鈕時,就調(diào)用store.dispatch(event: .onTriggerLogin)。 - 在
ViewController的deinit方法里面注銷訂閱store.unsubscribe(subsriptionID)。
總結(jié)
本節(jié)主要介紹了 純函數(shù) 和 附加作用,期間還演示如何用 純函數(shù) 做狀態(tài)管理的。最后還演化出了一個極簡版的 Redux。希望大家可以從中獲益!