MVVM架构如今在iOS开发中已经很常见了,网上关于MVVM架构的文章也很多,但是绝大多数的资料在讲解ViewModel和ViewController之间的互相绑定关系时,并没有从整体上构建出一个架构。KickStarter的iOS客户端代码是去年开源的,笔者刚开始入门RAC的时候并没怎么理解,现在过了几个月熟悉了RAC的那一套后,回过头来再看代码发现KickStarter的MVVM架构结构非常清晰,而且将大部分的逻辑代码很好的从ViewController中抽离了出来,下面就简单描述下KickStarter的MVVM架构

假设有一个ViewModel,然后定义两个protocol,一个是ViewModelInput,一个是ViewModelOutput
protocol ViewModelInput:提供了一些可供ViewController调用的函数。
protocol ViewModelOutput:能够绑定的变量,供ViewController观察。

然后ViewModel实现了ViewModelInput和ViewModelOutput协议。代码看上去大概是这个样子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
protocol ViewModelInput {
// 这里定义供ViewController调用的函数
}
protocol ViewModelOutput {
// Signal,Property等可供绑定的变量
// 当ViewController中调用Input协议中的函数后,经由ViewModel内部逻辑处理后通过ViewModelOutput
// 的一些值,反馈给ViewController,更新视图。
}
class ViewModel: ViewModelInput, ViewModelOutput {
// 实现 Input -> 逻辑处理 -> Output
var inputs: ViewModelInput { return self }
var outputs: ViewModelOutput { return self }
// 不定义上面两个变量也行,但定义之后,在VC中可以更好的区分数据的流向
}

实现了ViewModel之后,我们就需要将ViewModel和ViewController绑定起来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class ViewController: UIViewController {
var viewModel: ViewModel()
func bind(){
self.viewModel.outputs.loadDataSignal.observeValue { [weak self] value in
guard let sSelf = self else { return }
// 更新页面
}
// 这里将ViewModel的output的结果与ViewController绑定,当结果发生变化后自动更新VC
}
func loadData() {
// 这里调用viewModelInput的函数,比如刷新页面的数据
self.viewModel.inputs.loadData()
}
}

这样的话在ViewController中大部分逻辑就被分离到ViewModel中去了,而且数据的流向非常清晰。

1
ViewController -> inputs -> ViewModel -> outputs -> ViewController

这样的ViewModel是很容易测试的,在测试例子中定义好各种input后,可以根据outputs的结果来判断代码逻辑的正确与否。

KickStarter源码地址