Rx+MVVM 实际应用

文章目录
  1. 1. 前言
  2. 2. 为什么要在 MVVM 中使用 Rx
  3. 3. cell 监听复用问题

在看文 RxSwift 相关官方文档以后,知道 RxSwift 提供的多种功能以后,在实际场景中我们该怎么使用呢?

前言

本篇文章只谈 rx 在 MVVM 中如何使用。
RxSwift 的功能强大,加上 Apple 提供的framework也提供了很多方式,来处理 MVVM 中事件监听回调……
使用 RxSwift 的优缺点不谈,单单说 RxSwift 代码零散在 View层,ViewModel层就让人困扰,到底什么样的事件流才是最合适的,这个因人而异,不过在团队开发中框架使用的方式要统一!
这样可以大大提高开发效率……

MVVM 复合模式的样子

1
2
3
4
5
6
7
8
9
10
11
ViewController
* 管理视图布局渲染
* 控制器生命周期
* 接受视图 actions,并作出相应
* 控制器跳转
* ....

ViewModel
* 各种 Server 的中介器
* netServer,DBServer,OtherManager……
* ....

为什么要在 MVVM 中使用 Rx

抛去上段补充信息,只关注event/data的流动方向,得出下图

那么可不可以把 input/output 当做槽口

槽口!,如果看 RxSwift 源码可以看到大量的 sink(水槽) 类,sink 是连接各种 Observable 和 Observer 的管道:

Observable 流进水槽的槽口,流出(event(data?))
Observer 流出水槽的槽口,接受(event(data?))
所以,使用 RxSwift 来制作 View层和ViewModel 层的槽口,RxSwift vs 事件/数据的流程 vs App 流程

1
2
3
4
5
6
protocol ViewModelType {
associatedtype Input
associatedtype Output
var input: Input { get }
var output: Output { get }
}

一个详细的 demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
final class SayHelloViewModel: ViewModelType {
let input: Input
let output: Output

struct Input {
let name: AnyObserver<String>
let validate: AnyObserver<Void>
}

struct Output {
let greeting: Driver<String>
}

private let nameSubject = ReplaySubject<String>.create(bufferSize: 1)
private let validateSubject = PublishSubject<Void>()

init() {
let greeting = validateSubject
.withLatestFrom(nameSubject)
.map { name in
return "Hello \(name)!"
}
.asDriver(onErrorJustReturn: ":-(")

self.output = Output(greeting: greeting)
self.input = Input(name: nameSubject.asObserver(), validate: validateSubject.asObserver())
}
}

Subject是私有的。没有非法侵入ViewModel的方式,只能通过公共的Input和output属性。

ViewModel 可以插入到任何view,容易单元测试和Rx绑定。
view 使用这种方式绑定的 demo

cell 监听复用问题

cell 引用的 Observable tableView:cellForRowAtIndexPath: 中被订阅,该方法反复调用的过程 cell 不会被释放,只是被复用,所以每次 cell 在该方法中订阅的时候都应该先解除上一次的订阅。如果不解除,这个 Observable 会有多个订阅者

那么如何有效的解除上次 cell 复用产生的订阅呢?

思路一:有三种实现方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
override func prepareForReuse() {
super.prepareForReuse()
// Clean Rx subscriptions
disposeBag = nil
}

func bind(to viewModel: ViewModel) {
let bag = DisposeBag()
nameTextField.rx
.text
.orEmpty
.bind(to: viewModel.input.name)
.disposed(by: bag)
disposeBag = bag
}

or

1
2
3
4
5
override func prepareForReuse() {
super.prepareForReuse()
// Clean Rx subscriptions
disposeBag = DisposeBag()
}

第三那种 使用 swizzle cell-rx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
extension UITableViewCell: SelfAware {
@objc func rx_prepareForReuse() {
self.rx_prepareForReuse()
rx_reusableDisposeBag = DisposeBag()
}
public class func swizzle() {
// make sure this isn't a subclass
guard self === UITableViewCell.self else { return }
self.swizzleMethodForSelector(#selector(self.prepareForReuse), withMethodForSelector: #selector(self.rx_prepareForReuse))
}
public static func awake() {
UITableViewCell.swizzle()
}
}

or 自定义 Rx extension,使用 takeUntil(rx.prepareReuse)

引用:

RxSwift + MVVM: how to feed ViewModels