- 原文地址: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
MVVM 表明 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、前端、后端、区块链、产品、设计、人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划、官方微博、知乎专栏。