- 原文地址:MVVM + RxSwift on iOS part 1
- 原文做者:Mohammad Zakizadeh
- 譯文出自:掘金翻譯計劃
- 本文永久連接:github.com/xitu/gold-m…
- 譯者:iWeslie
- 校對者:swants
在本文中,我將介紹 iOS 編程中的 MVVM 設計模式以及 RxSwift。本文分爲兩部分,第一部分簡要介紹了設計模式和 RxSwift 的基礎知識,而在 第二部分 裏,有一個實現了 MVVM 和 RxSwift 的示例項目。html
首先,咱們爲何要使用設計模式呢?簡而言之,就是爲了不咱們的代碼亂成一團,固然這不是惟一的緣由,其中有一個緣由是可測試性。設計模式有不少,咱們能夠指出幾個很是受歡迎的模式:MVC、MVVM、MVP 和 VIPER。下面的圖片將這幾個設計模式的分佈協做性,可測試性和易用性進行了比較。前端
這些設計模式都有本身的優缺點,但最終它們都能使咱們的代碼更清晰、簡單而且易於閱讀。本文重點介紹 MVVM,我但願你能在閱讀完 第二部分 後着手實現它。react
讓咱們簡單介紹一下 MVC,而後繼續討論 MVVMandroid
蘋果官方建議使用 MVC 進行 iOS 編程,若是你有必定的 iOS 開發經驗,你可能會熟悉 MVC。這個模式由 Model、View 和 Controller 組成,其中 Controller 負責將 Model 鏈接到 View。理論上看起來 View 和 Controller 是兩個不一樣的東西,但在 iOS 的世界中,不幸的是,大多數狀況下它們是一回事。固然,在小型項目中,一切彷佛都符合規律,可是一旦你的項目變得龐大,Controller 因實現了大部分業務邏輯而變得臃腫,這會致使代碼變得混亂,可是若是你能正確編寫 MVC,並儘量地把 Controller 裏的東西解耦,大多數狀況下這個問題將獲得解決。ios
官方文檔中的 MVCMVVM 表明 Model、View 和 ViewModel,其中,View 和業務邏輯實現了 Controller,View 以及動畫,ViewModel 裏則是 api 的調用。實際上 ViewModel 這層是 Model 和 View 之間的接口而且它給 View 提供數據。有一點要注意的是,若是你在 ViewModel 的文件中看到如下代碼,那你多是在某處犯了一個錯誤:git
import UIKit
複製代碼
這是由於 ViewModel 不該該和 View 有任何牽連,在 第二部分 中咱們將藉助一個例子來研究這篇文章。github
MVVM 的一個特性是數據和 View 的綁定,而 RxSwift 就很完美地實現了這一點。固然,您也可使用 delegate,KVO 或閉包執行此操做,但 Rx 的有一個特性就是,它是一種思想,在不少語言裏通用,所以它與編程語言關係並不大。你能夠在 這裏 找到它支持的語言列表。如今在這一部分咱們將解釋 RxSwift 的基礎知識,固然,它們也是 Rx 世界的基礎知識。而後在 第二部分 中,咱們將憑藉 MVVM 使用 RxSwift 建立一個項目。編程
既然 RxSwift 是基於響應式編程的,那這到底是什麼意思呢?swift
在計算機中,響應式編程或反應式編程(Reactive programming)是一種面向數據流和變化傳播的編程範式。這意味着能夠在編程語言中很方便地表達靜態或動態的數據流,而相關的計算模型會自動將變化的值經過數據流進行傳播。—— 維基百科後端
也許你在讀完後對本段的任何內容仍是不怎麼了解,那下面咱們就經過如下的例子來進一步理解它:
假設你如今有三個變量(a,b,c):
var a: Int = 1
var b: Int = 2
var c: Int = a + b // 輸出 3
複製代碼
如今若是咱們將 a
從 1 改成 2 而且咱們打印 c
,它的值仍然是 3。可是在響應式編程的世界中一切都變得不同了,c
的值取決於 a
和 b
,這意味着若是你把 a
從 1 改成 2,那 c
的值就會自動從 3 變爲 4 而不須要你自行更改。
var a: Int = 1
var b: Int = 2
var c: Int = a + b // 輸出 3
a = 2
print("c=\(c)")
// 輸出 c=3
// 在響應式編程中 c=4
複製代碼
如今讓咱們開始學習 RxSwift 的基礎知識:
在 RxSwift(固然還有其餘 Rx)的世界中,一切事物都是事件流,其中包括 UI 事件和網絡請求等等。請切記這一點,我將用現實生活中的例子來解釋:
你的手機是一個 可觀察對象(Observable),它會產生一些事件,例如鈴聲或者推送通知等,這會讓你引發注意,事實上你訂閱(subscribe)了你的手機,並決定如何處理這些事件,好比你有時候刪除或者查看一些通知,事實上這些事件是一些 信號(signal),而你是作出決定的 觀察者(Observer)。
下面讓咱們來代碼來實現它:
在 Rx 世界中,一些變量是 Observable,而另外一些是 Observer(或訂閱者)。
所以 Observable 是通用的,若是它確遵循了 ObservableType 協議,你能夠監聽你想要的任何類型。
如今讓咱們定義一些 Observable:
let helloObservableString = Observable.just("Hello Rx World")
let observableInt = Observable.of(0, 1, 2)
let dictSequence = Observable.from([1: "Hello", 2: "World"])
複製代碼
在上面例子中,咱們分別定義了 Observable 類型的 String,Int 和 Dictionary,如今咱們應該 訂閱 咱們的 Observable,這樣咱們就能夠從發出的信號中讀取信息。
helloObservableString.subscribe({ event in
print(event)
})
// 輸出:
// next(Hello Rx World)
// completed
複製代碼
你可能在想輸出爲何會出現 next
和 completed
,爲何 ‘hello world’ 就不能好好打印一個字符串,我得說這就是 Observable 最重要的特性:
實際上每一個 Observable 都是一個 序列,與 Swift 裏 Sequence 的主要區別在於 Observable 的值能夠是異步的。若是你不理解這點並不重要,可是但願你能理解下面的描述,我以圖的方式呈現了這一特性:
在上面的圖中,咱們有三個 Observable,第一行是 Int 類型,從 1 數到 6。在第二行是 String,從 ‘a’ 到 ‘f’,隨即發生了一些錯誤。最後一行是 Observable 類型的手勢,它尚未完成,還在繼續。
這些顯示 Observable 變量事件的圖像叫作大理石圖。想要了解更多信息,您能夠訪問 這個網站 或從 App Store 下載 這個 App(它也是開源的 👍😎,這裏 有 App 的源代碼)。
在 Rx 世界中,對於每一個 Observable,都是由 3 種可能的枚舉值組成:
.next(value: T)
.error(error: Error)
.completed
當 Observable 添加值時,調用 next
並經過相關的 value 屬性(1 到 6,‘a’ 到 ‘f’)將值傳遞給 Observer(或訂閱者)。
若是 Observable 遇到了錯誤❌,則發出錯誤事件而後完成(‘f’ 以後的 X)。
若是 Observable 完成,則調用 completed 事件(6 以後)。
若是你想要取消訂閱一個 Observable,咱們能夠調用 dispose 方法,或者若是你想在你的 View deinit 的時候調用這個方法你應該使用 DisposeBag 在你的類反初始化時來進行你想要的操做。在這裏強調一點,若是你忘記使用 dispose 的話會致使內存泄漏☠️💀。例如,你應該這樣訂閱 Observable:
let disposeBag = DisposeBag()
helloObservableString.subscribe({ event in
print(event)
}).disposed(by: disposeBag)
複製代碼
如今讓咱們看看將 Rx 與函數式編程相結合有多完美。假設你有 Observable 的 Int 而且你訂閱了它,如今 Observable 會給你一堆 Int,你可使用不少方法改變來自 Observable 的發出信號,例如:
你可使用 map 方法讓信號在到達訂閱者以前作出一些改變。例如,咱們有 Observable 的 Int,它發出了 2,3,4 三個數字,如今咱們想要它們在傳給訂閱者以前都乘以 10,咱們能夠這麼作:
Observable<Int>.of(2, 3, 4).map { value in
return value * 10
}.subscribe(onNext: {
print($0)
}).disposed(by: disposeBag)
// 輸出:20 30 40
複製代碼
您可能又會想是否能讓它們在傳給訂閱者以前過濾掉一些值,例如,過濾掉示例中大於 25 的數字:
Observable<Int>.of(2, 3, 4).map { value in
return value * 10
}
.filter( return $0 > 25 )
.subscribe(onNext: {
print($0)
}).disposed(by: disposeBag)
// 輸出:30 40
複製代碼
又好比您有兩個 Observable 對象,而且您但願將它們合二爲一:
在上面的例子中,Observable A 和 Observable B 被組合在一塊兒並造成一個新的 Observable:
let sequenceA = Observable<Int>.of(1, 2)
let sequenceB = Observable<Int>.of(1, 2)
let sequenceOfAB = Observable.of(sequenceA, sequenceB)
sequenceOfAB.flatMap { return $0 }.subscribe(onNext: {
print($0)
}).disposed(by: disposeBag)
複製代碼
這兩個方法是搜索中最有用的方法之一。例如,用戶想要搜索單詞,您可能在用戶輸入每一個字符時都調用搜索 API。若是用戶快速鍵入,這樣的話你就會進行不少沒必要要的請求。爲了達到此目的,正確的方法應該是在用戶中止鍵入時調用搜索 API。這時您可使用 debounce:
usernameOutlet.rx.text
.debounce(0.3, scheduler: MainScheduler.instance)
.subscribe(onNext: { [unowned self] text in
self?.search(withQuery: text)
}).addDisposableTo(disposeBag)
複製代碼
在上面的例子中,若是用戶名 TextField 的內容在 0.3 秒內發生變化,則這些信號不會到達訂閱者,所以不會調用搜索方法。只有當用戶在 0.3 秒後中止輸入,訂閱者纔會收到信號並調用搜索方法。
distinctUntilChanged 功能對變化很敏感,這意味着若是兩個信號在信號沒有變化以前獲得相同的信號,它將不會被髮送給用戶。
Rx 世界比你想象的要大得多,我在 文章的第二部分 中講述了我認爲須要的一些基本概念,裏面有一個使用 RxSwift 的實際項目。
來自 raywenderlich 的 RxSwift 入門文章很是棒,我強烈推薦閱讀。
你可能不會在文章中注意到 RxSwift,由於它是 Swift 的高級概念之一,你可能天天都要閱讀不一樣的文章才能弄明白它。您能夠經過 此連接 看到 RxSwift 部分中的幾篇好文章。
你能夠經過 文章的第二部分 將 Rx 引入到 MVVM 的實際項目中,由於經過實例你將更好、更容易地理解 RxSwift 的概念。
你能夠經過 Twitter 或者發送 email 來聯繫到本文做者,郵箱是 mohammad_z74@icloud.com ✌️
若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。
掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。