iOS架構學習 - VIP總結
感謝@葉孤城在近一段時間組織的斗魚直播講解代碼的活動,一些開發技巧和工具讓我受益匪淺,再次感謝。
昨天是由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,看一下下面這張圖
首先數據的流向是單向的、職責都狠明確,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) } }
從上面看出這簡單的操作數據流向非常明確,模塊與模塊之間只有輸入和輸出,職責也非常明確,職責明確地好處就是調試方便、找問題可以集中到出問題的部分查找、節省時間。
至于單元測試,還不太清楚、之前寫代碼也沒怎么寫過單元測試,這塊知識還是空白狀態。