前不久咱們上線了一款新的 App - Glow Baby,App 針對 0 - 12 個月大的新生寶寶,提供爸爸媽媽全面、健康、科學的育兒知識,幫助記錄寶寶成長的點點滴滴。在 Glow Baby 的開發中,咱們也作了一些新的嘗試 - 使用 Swift 開發,並基於 Swift 的語言特色設計了新的 iOS App 架構。數據庫
除了 Community 這部分的代碼是做爲一個私有的 Repo 引入,Glow Baby 基本是 100% Swift 代碼。Glow Baby iOS 團隊都是第一次接觸 Swift,過程當中咱們踩過不少坑,遇到過不少抓狂的問題。但整體上,寫 Swift 更加有趣,全部的努力最終也證實是值得的:App 運行更加流暢,代碼更整潔可讀性更高,咱們開發效率也大大提升。編程
Baby App 跟 Glow 的其餘幾個 App 都是較爲複雜的 App,由於像日記記錄、本地存儲、網絡請求,服務器端數據的同步這些技術難點都有涉及。這些問題也都要求多線程編程。特別是數據同步,如何增量記錄數據的增刪改,什麼時機跟服務器端進行同步。解決這些技術難題是很是有意思的工做,也是架構設計的創造性和樂趣所在。設計模式
接下來的一系列文章咱們來看看 Baby App iOS 的應用架構。有些設計是基於 Swift 的語言特色的考慮,但並不妨礙總體的架構思路被應用在 Objective-C,甚至 Android 的 App 上。安全
在介紹 Glow Baby 的應用架構以前,先來看看目前 iOS 上最基礎的架構 MVC,以及爲解決 MVC 的毛病而誕生的其餘幾個架構,如 MVVM、VIPER 等。服務器
Cocoa 的不少技術跟架構都是基於 MVC。並且不管是文檔、示例代碼,仍是建立一個項目時提供的模板代碼,Apple 都鼓勵開發者去使用 MVC。MVC 定義了 App 裏對象的角色(Model-View-Controller),以及他們之間的交互方式:網絡
因爲 Controller 做爲粘合劑的存在,View 和 Model 只須要跟 Controller 交互,而不知道另外一方的存在。這樣,View 和 Model 做爲獨立可複用的組件,Controller 裏處理業務邏輯。聽起來這樣的架構很清晰直觀,實際應用中,MVC 對於不是很複雜的 App 也是很是高效的。但對稍複雜些的 App,MVC 使用起來就會很是吃力。多線程
你可能聽過 MVC 也被簡稱爲 Massive View Controller,這就是緣由所在 - View Controller 承擔的職責太多:架構
與單一責任準則(Single Responsibility Principle)背道而馳。過於臃腫的 View Controller 使 App 的維護成本很是高。咱們的第一個 App - Glow 其實就是這個樣子,儘管咱們已經把網絡請求以及數據訪問和存儲放到了 Model 裏,但因爲對象邊界的定義不夠清晰,大部分 View Controller 依然很臃腫,上千行的 View Controller 很常見。關於 View Controller 有個準則:若是一個 View Controller 超過了 300 行代碼,那它必定作了責任範圍之外的事。更不幸的是因爲一些職責移交給 Model,致使 Model 也變得臃腫起來。原來惟一能夠作 Unit Test 的 Model 如今測試也很困難。less
爲解決 Massive View Controller 的問題,MVVM、VIPER 等架構應運而生。這裏再也不詳細介紹這些架構,有興趣的讀者能夠自行去 Google。工具
Baby App 沒有使用 MVVM 和 VIPER。由於:
因此咱們結合本身的需求和 Swift 的語言特色設計了面向服務的架構(Service Oriented Architecture)。
面向服務的架構在服務器端的開發中很常見,它把業務分紅了多個邏輯獨立的組件。一個組件至關於一個 Service,封裝了與其業務相關的功能,如 UserService 負責用戶的註冊、登入等,而 BabyService 有 Baby 的增長、移除、以及數據的記錄等。Glow 服務器端的架構實際就是面向服務的。在 Baby App iOS 架構中引入 Service 的概念,是 App 開發過程當中迭代的結果,靈感也是來自咱們服務器端的架構。
能夠看到,Service 是對整個架構縱向邏輯切分的結果。拋開業務邏輯談 Service 意義不大,Service 一般與數據庫表的設計緊密相關。
橫向的邏輯切分將 Baby App iOS 的架構自上而下切分紅三個層(Layer):
服務層和數據層把複雜的邏輯封裝起來,做爲 Framework 提供接口給上層調用。應用層只能調用服務層暴露出來的接口,而不能直接調用數據層。層次結構增強了可重用性和可測試性。應用層調用服務層提供的簡單接口得到數據或者實行用戶操做。服務層也不須要知道數據層中網絡請求,服務器同步,以及數據持久化的具體實現。服務層,數據層,以及應用層都能很容易實現各自的單元測試(Unit Test)。
Framework 是很棒的工具。把服務層和數據層打包成 Framework,不只幫助構建解耦可重用的代碼,同時 App 的結構和業務邏輯也更加清晰。
應用層也能夠叫展現層(Presentation Layer),負責 UI 跟 展現邏輯。從 Code 角度說,就是 UIView 跟 UIViewController 的集合。複雜的邏輯都封裝到了下層,UIViewController 就變得十分輕量。在 Glow Baby 中,一個 View Controller 一般 200 至 300 行代碼之間,主要負責三件事:
從 Service 獲取的 ViewModel
實例並非 NSManagedObject
或者其餘持久化的 model 實例,跟 MVVM 中的 ViewModel 也不同。在 Baby App 中,它只是簡單的 Swift Struct,提供應用層須要的數據值。使用 Struct
的好處主要是:
使用 Struct 也就意味着想要底層持久化 Model 的更改放映到 UI 上,你必須經過 Service 再抓一次數據。也許有人認爲這是使用 Struct 的一個缺點。其實不是,這應該是優勢。由於 Immutable 的 ViewModel ,讓 View Controller 變得更加簡單,你不用擔憂其餘地方的代碼會更改你的 ViewModel 實例。調試起來也會更加方便,代碼更容易理解、可讀更高。WWDC 中有好幾個視頻都對 Struct 的使用和優點進行了詳解。
Baby App 支持 Theme,所以 Baby App 的 View Controller 還會調用 Theme 對象來設定 UI 的樣式。但對 View 樣式的設定都封裝在了 Theme 裏,因此並無增長太多代碼量及 View Controller 的複雜度。
服務層定義了一系列 Service 和供給應用層使用的 ViewModel。Service 封裝了 App 主要的業務邏輯,負責把底層持久化的 Model 和網絡請求返回的 JSON 轉換爲 ViewModel,再提供給應用層使用。這樣的分離即增強了 Immutablility 和 Statelessness,也讓應用層中的 ViewController 更輕量,只需幾行 Service calls。Service 雖然承擔大部分業務邏輯,但一個 Service 一般也就 300 行左右的代碼量,這得益於數據層的封裝和抽象。
數據層的做用是提供簡化的數據訪問接口,主要有 3 個模塊:
數據存儲咱們使用的是 Core Data,也能夠用 Realm 或者其餘數據庫代替。網絡請求咱們使用了 Moya 進行抽象,使 API 的設計和調用更簡潔,並支持咱們 Server 自定義的錯誤。數據同步模塊,會自動同步本地和服務器端的用戶數據。
在 iOS 上,MVC 因 Controller 的臃腫而遭到衆人詬病。但其實 MVC 做爲最基礎的設計模式,展示了一個架構的精髓 - 抽象分離。這是咱們應該學習思考的,而不是盲目從其餘架構模式中選擇一個來代替 MVC。Glow Baby iOS 的架構從能夠看做是一種 MVC。從總體看,數據層是 Model,服務層是 Controller,應用層是 View。而若是看細節的地方,應用層跟服務層提供的 ViewModel 也能夠看作一個 MVC:ViewModel - UIViewController - UIView.
設計架構也沒有「最好」或者「最正確」的方式,設計自己就是一項極具創造力的工做。但架構是有好壞區別,一個好的架構應該是對團隊成員最爲直觀,同時擴展性良好的架構。