iOS架构学习 - VIP总结

jopen 8年前

感谢@叶孤城在近一段时间组织的斗鱼直播讲解代码的活动,一些开发技巧和工具让我受益匪浅,再次感谢。

昨天是由36氪的iOS Team Leader@罗琦aidenluo讲解项目的架构, 有种茅塞顿开的感觉。

所以就在这里总结一下学习到的知识点。

项目地址是: https://github.com/aidenluo177/GitHubber

1、架构比较

一直以来自己都是遵循MVC模式,这个应该是苹果首推的开发架构,按照这个模式开发APP实现功能问题不大,但是存在的问题也非常明显,后期项目比较大了的话,Controller会便得越来越臃肿。不利于需求的改动和维护、后面入职的小伙伴也会经常看得一头雾水。

后来出现了MVVM、VIPER. 具体是什么就不介绍了,可以谷歌一下。

MVVM一般都会结合RAC来使用,之前了解过RAC,函数式编程的思想在某些地方用的是很方便、比如监听一些事件。 用RAC可以很快写出紧凑的代码,而且封装的很好,思路也很清晰。但是RAC的学习成本在我看来应该是最大的,看到一些开源的项目,一般都是部分功能用RAC去实现, 这里 有个纯RAC的项目,不过是个人作品。团队开发如果要每个人都熟悉RAC,估计成本太高。

VIPER 还没有真正在项目中使用过,看了这介绍的DEMO,分层有点多,有种过度设计的感觉。

2、视频里的VIP架构

VIP就是ViewController、Interaction、Present,看一下下面这张图

iOS架构学习 - VIP总结

首先数据的流向是单向的、职责都狠明确,Interaction做了一些Controller的逻辑操作、Presnet主要做一些转换的工作,例如一些数据的处理显示,因为很多时候数据拉取回来,并不能马上使用,通常需要而外转换一层,最后再把处理完成的数据丢会给Controller做显示的工作。

之前写代码很多时候数据都需要双向交互,这样的确有不确定性,开发久了有可能就忘了约定好的数据交互方式,不知不觉按照自己的逻辑开发,但是这个单向的数据流可以避免这个问题,因为有且只有一个方法,每个模块有输入和输出。

视频中作者把这个输入和输出的接口定义成一个协议(也就是接口),很清楚指明了数据的流向,每个协议要做的事情,

   protocol   TrendingViewControllerToInteractorPipline   {          func   refreshData  (request: TrendingDataRequest)         }        protocol   TrendingInteractorToPresenterPipline   {          func   presentData  (response: TrendingDataResponse)     }        protocol   TrendingPresenterToViewControllerPipline :  class   {          func   displayData  (viewModel: TrendingDataViewModel)         }  

每个模块里面用了typealias关键字重新命名了协议,为了区分不同的模块的协议方法,增加了可读性。

比如在ViewController里面:

  typealias  TrendingViewControllerInput = TrendingPresenterToViewControllerPipline       typealias  TrendingViewControllerOutput = TrendingViewControllerToInteractorPipline  

输入流只接受present的数据,方便present做好数据的转换之后,执行视图的渲染显示操作。

      extension TrendingViewController: TrendingViewControllerInput {        func displayData(viewModel: TrendingDataViewModel) {    data.removeAll()    viewModel.list.forEach { data.append( $0 ) }    refreshControl?.endRefreshing()    tableView.reloadData()    }       }  

每个Controller里面都有初始化各个模块的工作,把interactor和presnet组织起来了

  private    func   setupVIP  ()   {     let  interactor =  TrendingInteractor ()     let  presenter =  TrendingPresenter ()    output = interactor    interactor.output = presenter    presenter.output =  self     }  

再来看看Interactor的工作,这里有一个请求的worker,做数据的刷新处理工作,至于怎么去请求可以不用知道,只管结果。然后把拉取回来的数据传递给presnet

  extension TrendingInteract or : TrendingInteractorInput {        func refreshData(request: TrendingDataRequest) {    worker . fetchTrendingData({ ( data )  ->   Void   in      self  . output . presentData( data )    }) { (err or )  ->   Void   in      // handle error     }    }       }  

Presnet做了数据的转换工作、生成了viewModel丢回给Controller

      extension TrendingPresenter: TrendingPresenterInput {        func presentData(response: TrendingDataResponse) {    //Transform etc...    let viewModel = TrendingDataViewModel(list: response.list.map {    var cellModel = TrendingDataViewCellModel()    cellModel.cellText =  $0 .name    cellModel.cellLink =  $0 .link    return cellModel    })    output.displayData(viewModel)    }       }  

从上面看出这简单的操作数据流向非常明确,模块与模块之间只有输入和输出,职责也非常明确,职责明确地好处就是调试方便、找问题可以集中到出问题的部分查找、节省时间。

至于单元测试,还不太清楚、之前写代码也没怎么写过单元测试,这块知识还是空白状态。

原文  http://www.liuchendi.com/2015/12/27/iOS/25_iOS架构/