引言

  没有代码的瞎掰都是耍流氓!   这里是我使用MVVM模式结合组件化所做的音乐类App小部分功能,包括歌单、搜索和播放。你可以在这里看到viewModel到底如何书写、如何与controller配合,组件化应该如何搭建等等细节。麻雀虽小五脏俱全,作为参照来讲应该是足够了的。   这个系列是我在实践中的一些总结。学习之路上翻阅了不少博客,却都是介绍概念的居多,几乎没见着有完整代码供后来的人参考的。我遇到了很多坑也跨过了很多坑,所以有了写这几篇博客的动机。这几篇博客是配合我的代码来的,阐述了我是怎么做的、我为什么要这么做,当然我的做法不见得都是对的,只是希望能借此抛砖引玉。   当然啦,基本的理论基础还是要有的,这第一篇先简单介绍一下。

什么是MVVM

  随着ReactiveCocoa和RxSwift的横空出世,MVVM,这一发源于微软的软件架构模式也大热了起来,众多iOSer对此有着大量的讨论。   我们先来看一眼MVC,Apple推荐的iOS App标准架构模式。

  view将用户请求通知给controller,controller通过更新model来反应状态的改变,model(使用KVO和Notification)通知controller来更新他们负责的view。然而实际开发中充斥着大量如下代码:

class MyView: UIView {
  var model: MyModel? {
    didSet {
      // 在这里更新view的状态
    }
  } 
}
复制代码

如此一来view和model就绑定在了一起,形成了紧耦合也就没有复用一说了。奇怪的是这种写法是那么的自然以至于大家都没有感觉到有什么不对劲~~   再来看看MVVM:

MVVM是对MVC的一种改进。它将view和controller都视作view,view不持有model而是和viewModel通信,model的持有者变成了viewModel。在这个架构中viewModel接收用户请求处理业务逻辑并更新model,然后再将处理结果(也就是对model的更新)通过通知传回view来同步状态。使用MVVM能很好的分离UI和业务逻辑,view不持有model使得复用变成可能,viewModel因为不含任何UI元素因而能和任何view组合使用,同时对viewModel的测试也相对变得简单。

  MVVM是兼容于MVC的,你可以在项目中既使用MVC又引入MVVM,它对现有的代码并没有任何侵入性。所以少年,如果你还没有使用过MVVM,赶紧去试试吧,就像Swift一样,一旦上手了以后就再也回不去Objective-C了~~

Rx系列和MVVM的关系

  为什么Rx系列出现之前很少听说MVVM?Rx系列出现之后MVVM就受到如此推崇?难道说没有Rx系列的支持就无法使用MVVM?或者说MVVM是专门为Rx系列设计的?相信大家或多或少有着这样的疑问,这里推荐阅读iOS 关于MVVM Without ReactiveCocoa设计模式的那些事。   简而言之,iOS中可以使用Notification,KVO来实现绑定,只是过程比较痛苦(KVO不仅繁琐且回调略显蛋疼),而反观Rx系列,它使用观察者模式:

  • 可以方便的创建事件流和数据流
  • 使用查询式的操作符组合和变换数据流
  • 可以订阅任何可观察的数据流并执行操作

这就使得数据绑定变得极其简单。正是因为Rx系列完美契合于MVVM模式,所以才导致MVVM模式的大热。RxSwift和ReactiveCocoa因为高度类似,掌握其中一种足以。我这边使用的是RxSwift,这里安利两篇文档给不熟悉的小伙伴们:

  • RxSwift中文文档
  • ReactiveX中文文档

什么是组件化开发

  一般我们在开发的时候都是新建一个Cocoa Touch Application,然后根据功能模块划分几个文件夹,比如Home/Discover/Profile/Vendor等等,这些文件夹又各自有着Controller/Model/Util之类的分层。看起来已经划分了功能模块,实际上这却是一个复杂的项目。所有的代码都在一个单一工程里,久而久之尤其是多人开发的时候,耦合会特别严重,你会发现去掉任何一个模块的代码,整个项目完全无法运行,并且即使你只想测试某个功能也不得不编译整个项目,组件化因此应运而生。   对于组件化开发,我的理解就是将一个庞大臃肿的单一工程, 根据功能或者属性进行分解,拆分成各个独立的模块或者说是组件,这一步和之前其实没有区别,区别在于每一个组件都可以单独维护、独立运行,然后可以根据业务需求,组织成一个拥有完整业务逻辑的工程。;组件化特别适合大型项目的开发,因为:

  • 便捷性:仅仅通过改变依赖就可以进行功能的增减
  • 独立性:每个组件可以单独编写、单独编译、单独运行和单独测试
  • 复用性:对于功能性,工具性的代码亦或是通用的控件等等,可以很轻松的复用

如何组件化

  组件化带来的好处是非常直观的,并且它实现起来并不复杂,使用CocoaPods可以很方便的实现组件化。关于CocoaPods的基本使用相信大家都是十分熟悉的,这里就不介绍了。

pod lib create #ModuleName#
复制代码

使用以上命令就可以在本地新建一个pod库。组件编写完以后你可以上传到远程服务器或者仅仅当作本地私有库使用。这里面唯一的坑点是集成第三方静态库,比如项目中使用到极光分享,在podsepc中写下:

s.dependency 'JShare'
复制代码

会发现CocoaPods报错。

The 'Pods-xx' target has transitive dependencies that include static binaries

复制代码

解决的办法在这里。简单来说就是我们新建一个Cocoa Touch Framework来包装这个静态库就可以。如果你也遇到了这个问题,这篇文章应该能够帮到你。

组件化中间层如何实现

  设计组件化中间层有两种比较有代表性的方案:

  • 基于URL注册跳转的方式,参考github.com/meili/MGJRo…
  • 基于Objective-C运行时的Mediator方式,参考casatwy.com/iOS-Moduliz…

  两种方式我都有尝试过,谈一点个人看法吧。URL注册的方式在使用上非常繁琐而且很多时候其实没有必要。首先每一个页面跳转都需要事先注册好URL,这里会牵涉到非常多字符串硬编码,很容易出错;其次两个有强关联的页面,比如从预览到详情,这两个页面总是会成对出现,你几乎没有其他选择会跳转其他页面,这种情况下个人感觉注册不注册URL都没有什么关系。基于runtime的Mediator方式,首先它不需要注册,省去了很多比对字符串的过程,当然,这种方式依然避免不了字符串硬编码,不过体量相对较小;其次它可以非常容易的传递各种参数来进行组建间通信。   我选择的是Mediator方式,也因为它易于实现,在模块的划分上我的做法粒度比较大,这些具体在代码里体现。

总结

  以上简要介绍了MVVM和组件化开发,我这里就说这么多了,接下来是show you the code环节。需要说明的是这是一份Swift代码,并且使用了RxSwift,如果你对这些都不熟悉,可能这个暂时还不太适合你。使用ReactiveCocoa的同学不要慌,首先RxSwift和ReactiveCocoa区别真没那么大,再者这里讲的是如何做,和使用的库关系不大,套用一下即可。   

下一篇讲的是代码逻辑,喜欢看干货的同学不要错过了哟。这里再次安利一下代码地址,如果觉得有帮助,点个star呗,又不会怀孕不是~~