本文翻譯自 The best architecture for the iOS app, does it even exist?,建議參考原文閱讀,也可查看這裏ios
前一段時間,我偶然發現了有關 iOS 體系結構模式的文章,標題頗具挑釁性:「惟一可行的 iOS 架構」。標題中問題的答案其實是 MVC。簡而言之,MVC 是 iOS 應用程序惟一可行的也是最好的架構。git
該文章的主要思想是人們只是以錯誤的方式去理解 MVC。該 ViewController 其實是表示層的一部分,而 Model 部分則表明整個 Domain Model,而不只僅是某些數據實體。總的來講,我贊成那個帖子的想法,可是若是我贊成那個帖子的每個陳述,我就不會寫這篇文章了,不是嗎?github
我注意到做者基本上沒有涉及格式良好的應用程序體系結構的一個很是重要的方面:使用單元測試(UT)覆蓋了應用程序業務邏輯(BL)。對我而言,這是明智的應用程序體系結構的最重要因素之一。若是沒法提取應用程序 BL 並以足夠的覆蓋範圍實現 UT,那麼這種架構簡直糟透了。數據庫
此外,若是一個應用沒有 UT,那麼證實上述觀點是不可行的,所以其架構最有可能出現問題。您能夠向本身保證,在你從緊張工做中的片刻休息時間能夠輕鬆實現UT,或者僅僅是由於您在 XCode 項目中擁有專用的「Tests」目標以及一些模板 UT。相信我,但這不過是一種幻想。我堅信,若是單元測試未隨功能一塊兒實施或在交付後不久就將沒法實施。swift
以該聲明爲公理,應用程序體系結構必須提供將 BL 與 UI 表示分離並使其可測試的功能。很明顯,因爲 UIViewController 子類對生命週期的依賴性,所以它不是該角色的最佳候選人。這意味着負責 BL 的類必須位於 UIViewController 和 Services 之間,或者換句話說,位於 View 和 Domain Model 之間。架構
值得注意的是,這裏的 Services 是指負責聯網,與數據庫、傳感器、藍牙、鑰匙串、第三方服務等進行通訊的邏輯。換句話說,是應用中多個位置、頁面的共享部分。而圖上的業務邏輯部分僅對應於一個頁面或一個由視圖控制器表示的頁面組件。在開頭提到的有關 MVC 的文章中,做者將 BL 和 Domain Model 部分結合在一塊兒,同時接受 UIViewController 是表示邏輯(即視圖)的一部分。app
如今,當肯定了將表示和業務邏輯分離的須要時,讓咱們考慮一下這兩個部分如何相互通訊。 這就是那些著名的架構模式出現的地方。框架
在 MVP 模式中,Presenter 和 View 經過協議相互連接。Presenter 被注入了 View 協議的實例,反之亦然,View 的協議必須具備足夠的接口才能在 UI 中呈現原始數據,而Presenter 的協議必須具備傳輸從用戶或系統接收到的事件(如觸摸,手勢,搖動等)的接口。UIViewController 子類在此處表示 View 部分,而 Presenter 類不能依賴UIKit(例如,有時須要導入 UIKit 才能對 UIImage 等數據類進行操做)。less
在 iOS 上,因爲 UIViewController 生命週期的工做方式,它必須具備對 Presenter 實例的強引用,而最後一個必須是弱引用,以免循環引用。此配置令人聯想到委託模式。 在大多數狀況下,UIViewController 可能具備對 Presenter 的直接類型化引用,但在某些狀況下,最後一個角色也能夠經過協議注入到第一個中。若是 presentation 用於不一樣的業務邏輯,這可能會頗有用。Presenter 到 UIViewController 的連接必須經過協議才能進行模擬,並用 UT 覆蓋。在 Service 部分,我不會作太多具體說明,可是爲了測試 Presenter,還必須將其與協議一塊兒注入。mvvm
有關 MVP 的更多詳細信息以及基本示例,請參見此處1。
在 MVVM 模式中,表示和業務部分使用響應性綁定相互通訊,它們分別稱爲 View 和 ViewModel。在 iOS 中,一般會使用 ReactiveCocoa,RxSwift 或現代的 Combine 框架進行響應性綁定,它們一般位於 ViewModel 類中,而且也由 ViewController 經過協議使用。在與 Services 或 Domain Model 進行通訊的一部分中,MVP 並無太大的區別,但人們可能更喜歡在這裏使用綁定或響應性事件。與前面的模式同樣,必須在協議中注入依賴項,以便在 UT 中模擬它們。
能夠在此處2找到有關 MVVM 的更多詳細信息以及基本示例。
這裏獨立的主題是路由。在 iOS 中,以模態方式顯示新屏幕或推送到導航堆棧是經過 UIViewController 子類來實現的。可是,這些操做多是 BL 的一部分,而且可能會被 UT 覆蓋,例如若是發生特定事件,則必須關閉屏幕。在這種狀況下,將應用程序邏輯的這一部分分爲一個稱爲 Router 的類是有意義的。所以,模式變爲 MVP+R 或 MVVM+R。在某些來源中,您可能會發現此部分分別命名爲 Coordinator 和 MVVP+C 或 MVVM+C。儘管協調器可能具備除路由以外的其餘一些邏輯,但我更喜歡在概念上將它們等同。ViewModel 和 Router 之間的連接必須經過協議,而且最後一個必須僅負責屏幕操做,全部 BL 必須仍然集中在第一個中。所以,Router 不是 UT 的主題。
具備 MVVM+R 架構模式實現的示例項目能夠在個人 GitHub3 上找到。
VIPER iOS 體系結構模式是 MVVM+R 的擴展,其中 ViewModel 分爲兩部分:Interactor 和 Presenter。第一個負責與實體(即域模型)的通訊。第二部分準備要在視圖中呈現的模型類。老實說,我從未使用過這種模式,由於對我而言,它彷佛過於分散和複雜。 MVVM+R 關注點分離對我而言老是足夠的。
在 MVVM+R 中,每一個模塊(屏幕)必須至少顯示 3 個類:ViewController,ViewModel 和 Router。而且必須有一個實例化全部這些部分並將它們彼此連接的位置,即模塊構建的關鍵。最合適的位置是 Router,由於它沒有與 iOS UIViewController 生命週期耦合,而且必須知道如何顯示頁面才能正確關閉它。可是,在某些狀況下,將這一部分移到名爲 Builder 的單獨的類中會更方便,這就是 RIB(Uber的架構模式)中發生的狀況。ViewModel 重命名爲 Interactor,其他部分保持不變。這種模式具備 Uber 引入的一些更有趣的想法和技術,您能夠在 RIB Wiki 上閱讀。可是,我在 RIBs 代碼庫中發現的最實用的東西是 XCode 模板,當在項目中引入新的 RIBlet 時,它能夠幫助避免樣板編碼。這些模板也能夠很容易地用於 MVVM+R 類。爲 Uber 的 iOS 工程師👏。
最後簡單聊聊關於 iOS 上的單向數據流架構模式。若是看一下以上模式的方案,它們在組件之間都具備雙向鏈接。在 Redux 中不是這種狀況。這種架構模式最初是從 Web 遷發到移動應用的,尤爲是 React 框架。在 ReSwift 框架中,此概念是 iOS 開發中最受歡迎的實現。我不會詳細介紹,由於還沒有在生產應用中使用此架構。可是,很明顯,從 Web 開發進入 iOS 的人們發現這種架構模式最爲直觀和熟悉。
什麼纔是最好的應用程序架構始終是一個熱門的主題,因此如今我更傾向於約翰·桑德爾在他的最近一次演講中提出的想法:
最好的架構是您和您的團隊共同建立的架構,經過將標準模式和技術與系統設計相結合來適合您的項目。
[1]https://medium.com/@saad.eloulladi/ios-swift-mvp-architecture-pattern-a2b0c2d310a3 [2]https://medium.com/flawless-app-stories/practical-mvvm-rxswift-a330db6aa693 [3]https://github.com/OlexandrStepanov/MVVM-RouterDemo