iOS中的VIPER一文通😁
今天来介绍一个适合构建大型APP或比较大的模块的架构-VIPER;
为什么用VIPER?
略
什么是VIPER?
伴随着业务的增加、功能模块的增涨我们会发现,代码的结构越来越不清晰、测试也越来越难,陷入了一个恶性循环;
为了使得代码便于维护、结构更清晰、便于测试,这时候我们就需要一种好的架构方式来组织我们的APP代码,这时候VIPER就应运而生;
VIPER这个概念来自于Clean Architecture,VIPER是View, Interactor, Presenter, Entity,Routing,这几个单词的缩写;在Clean Architecture中将整个APP或者说模块用专门的不同的层次来分隔,这使得各个层之间减轻依赖,每个层次负责单一的功能,也符合功能单一的设计原则,也更加的便于测试;
拆分VIPER
viper1.pngView
- 就是UIViewController/View
- 展示Presenter传递过来的数据
- 收集用户输入、或者事件等交互信息回调给Presenter
View是被动的,它等待Presenter传递内容用于展示,它从不主动去向Presenter请求数据;
Presenter
- 负责协调Interactor和View之间的逻辑
- 将Interactor传递过来的Model转转换为ViewModel
- 传递ViewModel给View
- 处理从View中传递过来的交互事件
- 在需要的时候,通过调用Router导航到新页面
Presenter是整个VIPER架构的核心,在Presenter中包含驱动UI的来逻辑,他知道什么时候展示什么界面;它负责收集从View中传递过来的数据或者事件,从而去驱动Interactor请求数据、或更新UI;当接收到Interactor传递过来的数据,在将数据转换成ViewModel后,将数据传递给View;
Interactor
- 包含业务逻辑、规则满足具体的业务需求
- 负责将Entity转换为Model
- 知道在数据传递给Presenter前需要执行哪些动作,比如存储、删除数据等
- 不关心具体的数据来源,通过其他的Server API来获得数据
Interactor包含Entity关联到具体业务的逻辑,比如只有符合某种条件的Entity才会被传递到Presenter,在其内部不应该和任何的UI产生依赖关系;因为Interactor负责的是纯业务逻辑,所以他应该很容易进行测试或者通过TDD进行开发;
Entity
- 业务数据模型
- 一般直接是没做过转换处理的接口返回数据
- 直接也仅和Interactor产生关联
Router
- 整个模块的入口及出口
- 一定包含一个方法负责创建当前模块,一般是返回一个关联整个模块的UIViewController;
- 负责页面的导航,从当前模块到其他模块,比如push、present到新的页面
在VIPER中,页面的切换响应是由Presenter和Router一起承担的,由于Presenter负责收集用户的输入,所以他知道什么时候需要切换到哪个新的页面,而Router内包含有具体的切换方法,知道怎样去切换;
我们也可以将Router中创建整个模块的功能分离出来,创建一个单独的Buider模块,这就是VIPER-B;
更加详细的划分关于VIPER-B,见下图(图片的来源再这里):
viper3.png其他模块介绍
Buider
- 整个模块的入口
- 包含一个创建方法负责创建当前模块,一般是返回一个关联整个模块的UIViewController;
Data Manager
- 是对负责网络请求的Server,本地存储Server的封装
- 不负责具体的网络请求或者存储
- 可以被多个Interactor订阅
Model
- 是对entity的封装
- 不是能够直接展示的数据,记得ViewModel的转换时Presenter的工作
ViewModel
- 由Presenter负责生成
- 包含有UI需要显示的内容
viper中各个模块的整合
我们知道了各个层次的职责,将所有的层次组合到一起才是一个完整的架构;怎样将这些不同的层整合到一起也是一个问题,根据简单复杂有两个办法;
方法1:
viper2.png- 像上图一样,view直接持有present,同时view成为present的代理,这样能够使得他们之间实现双向交互
- router直接被present持有
- interactor直接被presenter持有,interactor回调或者通知presenter数据进行更新
方法二:
同样通过协议代替上面的持有,将上面办法中的持有完全由代理代替,即Presentor、View各自成为各自协议的代理;但是需要注意避免循环引用及完全弱引用,其中一方的代理需要用强引用,一方代理需要弱引用;