引入 Store


class Store {

    // 观察者,用于响应状态更新,第一个 State? 为旧状态,第二个 State 为当前状态
    typealias Observer = (State?, State) -> Void

    private(set) var state: State  // 当前状态
    private var _observers: [UUID: Observer]  // 所有的观察者

    // 初始化
    init(initailState: State) {
        self.state = initailState
        self._observers = [:]
    }

    // 发出事件
    func dispatch(event: State.Event) {
        let oldState = self.state
        self.state = State.reduce(self.state, event: event)
        _publish(oldState: oldState, newState: self.state)
    }

    // 订阅状态更新
    func subscribe(observer: @escaping Observer) -> UUID {
        let subscriptionID = UUID() //  UUID 是唯一标识符,该 id 可用于取消订阅
        _observers[subscriptionID] = observer
        observer(nil, self.state) // 订阅时,将当前状态回放给该观察者
        return subscriptionID
    }

    // 取消订阅
    func unsubscribe(_ subscriptionID: UUID) {
        _observers.removeValue(forKey: subscriptionID)
    }

    // 私有方法,通知所有的观察者,状态已经更新了
    private func _publish(oldState: State?, newState: State) {
        _observers.values.forEach { observer in
            observer(oldState, newState)
        }
    }
}

如何使用 Store:


func useStore() {
    
     //首先下创建 Store
    let initailState = State(username: "",password: "",loading: false,data: nil,error: nil)
    
    let store = Store(initailState: initailState)

    // 然后,订阅程序状态,并且将这些状态录制下来
    // 以下变量 newStates 和 oldStates 用于录制状态历史
    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)
    
    // 最后,比对录制的状态是否符合预期
    // 描叙预期
    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)
    ]
    
    // 比对结果
    newStates == expectNewStates // 结果:true 😎
    oldStates == expectOldStates // 结果:true 😎
}

这就是如何在测试环境里面使用 Store,那么在 App 里面如何使用 Store 呢。 一个 相对简单(并未优化) 的方法,就是将 Store 注入到对应的组件里面,这里以 ViewController 为例:

  • ViewController 可以使用 store.subscribe 方法订阅程序的状态。当状态更新时,比对新旧状态,然后刷新过时了的 UI。
  • 当用户触发某个事件时,调用 store.dispatch 方法将事件发出去,如:当用户点击登录按钮时,就调用 store.dispatch(event: .onTriggerLogin)
  • ViewControllerdeinit 方法里面注销订阅 store.unsubscribe(subsriptionID)

总结

本节主要介绍了 纯函数 和 附加作用,期间还演示如何用 纯函数 做状态管理的。最后还演化出了一个极简版的 Redux。希望大家可以从中获益!

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 项目中如果配置比较多的话,store的使用可能不只是在store文件夹里面的文件。比如下面项目结构: 例如希望在a...
    liwuwuzhi阅读 23,844评论 4 7
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,163评论 1 32
  • 1.NSTimer不准时的原因:(1).RunLoop循环处理时间,每次循环是固定时间,只有在这段时间才会去查看N...
    稻春阅读 1,291评论 0 3
  • 1.设计模式是什么? 你知道哪些设计模式,并简要叙述?设计模式是一种编码经验,就是用比较成熟的逻辑去处理某一种类型...
    龍飝阅读 2,234评论 0 12
  • 很长一段时间,内心被阴郁的乌云压着,好像没有什么可以轻易的取悦自己,精美的食物,流行的服饰,热播的剧集,都无法给你...
    静思甜阅读 519评论 0 1