Sucha's Blog ~ Archive for February, 2021
21年2月16日 周二 18:24
Swift(3) - PageEventBus
春节在家,继续思考了一下业务上的一些痛点,并做了整理,趁春节期间的空闲,搞了一个改善页面内通讯,限定 UI 及业务逻辑绑定关系的库 PageEventBus。
先说一下之前在业务上觉得不爽的点:
- 页面内非常多的输入组件、点击开关,以及可改变当前页面显示内容的可选项
- 这些点击开关、可选项的变化会导致页面展示内容的变化,以及输入组件的刷新,以及精度展示的配置
- 这些点击开关、可选项、显示内容、输入组件可能处于页面内各种不同层级的 UI 中,比如可以是 child view controller 等
说一下之前的处理方法,使用 RxSwift,各种不管 UI 层级多深的 binder、controlProperty、controlEvent,都透过多层 UI,传递到业务 view controller 这一层来做绑定,带来的问题,有以下几个
- 业务 view controller 急速膨胀
- 穿透用的 UI 层级,不得不写 controlEvent、binder,但实际上是其 subviews 进行处理
- 当业务变化时,每一层涉及到的 UI、业务 view controller 都要做出修改
- UI 绑定了业务,复用很难
基于上面的问题,我有了一些思考,希望有下面这样的东西
- UI 跟业务分离,业务逻辑能够操作 UI,UI 独立可复用
- 具体的业务可以分割到不同的业务单元处理,接收消息,可以将处理结果传递出来
- 当业务需求变化时,希望不会因为输入、输出参数的变化,而修改 UI
就想着能否用上一个页面内的总线,将参数通过总线传递,这样可以对抗业务需求上导致的参数变化,比如 view model 可以接收总线消息,也可以发送消息,有了总线后,可以不需要关心 UI 层级了,也没有参数需要透传了。
另外,UI 的输入、输出抽象到业务逻辑 view model 的输入、输出,可以认为 view 持有了 view model,view model 持有 unowned view 的不可变引用;对于 view controller,也可以相应的持有 page model。这样,UI 总是可以复用的,而 view model,才处理实际业务,如果多个业务本身可以抽象到更高的层级,那么显然 view model 也是可以做到的。
还加上改进的一点,由于上面接收、发送事件的角色都在系统展示树上,可以利用 responder chain 链,在 view didMoveToWindow 或者 controller viewDidAppear 的时机,做事件总线的绑定,意味着总线本身都不需要传递,可以自动连接上。
上面的一些思考,加上春节空余时间的实践,就是上面说到的 PageEventBus。
一些代码上的具体实践:
- 基类是 BlockEventAgent<E,R>,泛型的两个参数,一个是输入类型,一个是输出类型,可以用 enum 分类限定
- BlockEventAgent 拥有名字,当它们接收到消息,返回处理结果的时候,可以知道是谁返回的
- BlockEventBus 很简单,添加 agent、以及给各个 agent 发送消息,搜集结果并返回给调用方
- BlockViewModel<V:UIView,E,R> 泛型限定了具体操作的 view,创建的时候就需要输入 view 实例,其生命周期小于等于 view
- BlockPageModel<C:UIViewController,V:UIView,E,R> 绑定了 view controller,可以选择生成 event bus,或者使用已有的
- BlockView 和 BlockViewContnroller 都加入了在 didMoveToWindow 或 viewDidAppear 激活搜索已有 event bus 的逻辑
话说这一整套限定的东西还是很多的,我准备使用 event bus 来传递 view model,view model 内部还是使用 RxSwift 来做业务绑定,view model 带 disposeBag,或者使用 view 的 disposeBag 也是可以的。