什么是VIPER
简而言之是一种代码结构,与MVC 和 MVVM等结构相比,VIPER分的更加精细,易于拓展,其主要角色主要有五种:
V(View): 渲染层
I(Interactor):网络请求层,数据更新等
P(Presenter): 逻辑层,组装者
E(Entry): 数据层
R(Router): 跳转层
调用关系如下:
这里这么说可能会比较抽象, 下面举个简单例子吧
实践
我们以列表的上下滑为例,那么怎么抽取一个可以通用的VIPER结构的列表组件呢?
presenter
首先分析presenter的主要作用是封装列表的数据及逻辑,那么对外暴漏的一定是一些基础功能的结构,比如触发上拉加载,下拉加载等,那么对presenter的功能可以用下面的protocol来定义:
/// Presenter 提供的交互接口,供 “View” 层调用
public protocol ListPresenterProtocol {
/// viewDidLoad 时调用
func prepareList()
/// 加载更多
func handleScrollDownToFetchMore()
/// 下拉刷新
func handlePullToRefresh()
func hasMore() -> Bool
}
/// Presenter 提供的数据接口,供 “Interactor” 层调用
public protocol ListPresenterDataSource {
/// 数据的增删改查
func append(...)
...
}
如上外部使用的话只需要在触发上拉和下拉的时候调用接口即可
interactor
接下来是逻辑层,这部分需要封装的是缓存逻辑,接口请求,数据拼装等,简单来讲定义如下吧
open class ListInteractor : NSObject {
public var usingCache : Bool = false // 默认不使用缓存
public weak var presenter : ListPresenterDataSource? // 对presenter层的弱持有
public let loader : LoaderProtocol //接口请求实例
public init(loader: LoaderProtocol) {
self.loader = loader
}
}
这里封装了缓存,请求等逻辑,在loader请求到数据之后会通过presneter的弱引用对象来对数据层进行增删改查。
entry
那么enery是放在哪的呢,实际上是放在presenter里面的,我们看下一个具体的presenter的实现是啥样的
open class ListPresenter: ListPresenterDataSource, ListPresenterProtocol {
/// 什么是changeSet?
public var changeSetHandler: ListChangeSetHandler?
public let listInteractor: ListInteractorInput
/// 下面是数据的具体存储
public var defaultSection = SectionData(sectionTitle: "default")
public var sectionData = [SectionData]()
/// 组装interactor
public init(interactor: ListInteractor) {...}
}
具体的数据流向是下面这样的:
View[触发action] ----> Presenter[调用handlePullToRefresh] ----> Interctor[调用load] ----> 网络请求回调数据 ----> Presenter[update Entrys] -----> update View
怎么更新View
上面的调用关系基本清晰了,问题是怎么去更新view这一层呢?
我们知道列表要通用的话,最常见的有tableview和collectionview,其实还有一些第三方的列表更新逻辑,比如ComponentKit,那么我们怎么去更新不同的view还能达到复用呢?
这里发挥作用的就是 changeSet 这个角色了。
/// 这个是 View 层的协议,用于适配 各种
@protocol ListChangeSetHandler <NSObject>
- (void)applyChangeSet:(ListChangeSet * _Nonnull)changeSet;
@end
/// ListChangeSet 其实里面定义了各种array和dictionary,用于生成列表更新的时候所需要的各种model
怎么用呢?这个作用是啥呢?
首先我们要明白Presenter是个组装者,在初始化的时候就定义了这个列表所需要的各个角色的具体类
举个栗子:
setupLoader()
// 生成具体的interactor并搭配具体的网络请求层
let interactor = ListInteractor(loader: loader as DataLoaderProtocol)
// 生成了具有特定网络请求的presenter
presenter = ListPresenter(interactor: interactor)
// 设置具体的view层,这里是一个应用于tableView的changeSet层
presenter.changeSetHandler = TableViewChangeSetHandler(tableView: tableView)
通过上边就能把view的刷新层定义成一个可复用的啦,下次定一个tableview的列表时,是不是就可以直接用上边的changeSet啦!
总结
VIPER 对业务角色抽象的更加具体,也有利于单元测试,有利于抽象不同的业务类型,而且对于特别复杂的项目还可以抽象更多的角色来实现自己的功能分类。