[译] iOS 里的 MVVM 和 RxSwift

简介: 在本文中,我将介绍 iOS 编程中的 MVVM 设计模式以及 RxSwift。本文分为两部分,第一部分简要介绍了设计模式和 RxSwift 的基础知识,而在 第二部分 里,有一个实现了 MVVM 和 RxSwift 的示例项目。

原文地址:MVVM + RxSwift on iOS part 1
原文作者:Mohammad Zakizadeh
译文出自:掘金翻译计划
本文永久链接:github.com/xitu/gold-m…
译者:iWeslie
校对者:swants


在本文中,我将介绍 iOS 编程中的 MVVM 设计模式以及 RxSwift。本文分为两部分,第一部分简要介绍了设计模式和 RxSwift 的基础知识,而在 第二部分 里,有一个实现了 MVVM 和 RxSwift 的示例项目。

设计模式:

首先,我们为什么要使用设计模式呢?简而言之,就是为了避免我们的代码乱成一团,当然这不是唯一的原因,其中有一个原因是可测试性。设计模式有很多,我们可以指出几个非常受欢迎的模式:MVC、MVVM、MVP 和 VIPER。下面的图片将这几个设计模式的分布协作性,可测试性和易用性进行了比较。
image.png

这些设计模式都有自己的优缺点,但最终它们都能使我们的代码更清晰、简单并且易于阅读。本文重点介绍 MVVM,我希望你能在阅读完 第二部分 后着手实现它。
让我们简单介绍一下 MVC,然后继续讨论 MVVM

MVC:

苹果官方建议使用 MVC 进行 iOS 编程,如果你有一定的 iOS 开发经验,你可能会熟悉 MVC。这个模式由 Model、View 和 Controller 组成,其中 Controller 负责将 Model 连接到 View。理论上看起来 View 和 Controller 是两个不同的东西,但在 iOS 的世界中,不幸的是,大多数情况下它们是一回事。当然,在小型项目中,一切似乎都符合规律,但是一旦你的项目变得庞大,Controller 因实现了大部分业务逻辑而变得臃肿,这会导致代码变得混乱,但是如果你能正确编写 MVC,并尽可能地把 Controller 里的东西解耦,大多数情况下这个问题将得到解决。
image.png

官方文档中的 MVC

MVVM:

image.png

MVVM 代表 Model、View 和 ViewModel,其中,View 和业务逻辑实现了 Controller,View 以及动画,ViewModel 里则是 api 的调用。实际上 ViewModel 这层是 Model 和 View 之间的接口并且它给 View 提供数据。有一点要注意的是,如果你在 ViewModel 的文件中看到以下代码,那你可能是在某处犯了一个错误:

import UIKit

这是因为 ViewModel 不应该和 View 有任何牵连,在 第二部分 中我们将借助一个例子来研究这篇文章。

RxSwift:

MVVM 的一个特性是数据和 View 的绑定,而 RxSwift 就很完美地实现了这一点。当然,您也可以使用 delegate,KVO 或闭包执行此操作,但 Rx 的有一个特性就是,它是一种思想,在很多语言里通用,因此它与编程语言关系并不大。你可以在 这里 找到它支持的语言列表。现在在这一部分我们将解释 RxSwift 的基础知识,当然,它们也是 Rx 世界的基础知识。然后在 第二部分 中,我们将凭借 MVVM 使用 RxSwift 创建一个项目。

响应式编程:

既然 RxSwift 是基于响应式编程的,那这究竟是什么意思呢?

在计算机中,响应式编程或反应式编程(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)。
image.png

下面让我们来代码来实现它:
Observable 和 Observer(订阅者):
在 Rx 世界中,一些变量是 Observable,而另一些是 Observer(或订阅者)。
因此 Observable 是通用的,如果它确遵循了 ObservableType 协议,你可以监听你想要的任何类型。
image.png

现在让我们定义一些 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 的值可以是异步的。如果你不理解这点并不重要,但是希望你能理解下面的描述,我以图的方式呈现了这一特性:
image.png

在上面的图中,我们有三个 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:

你可以使用 map 方法让信号在到达订阅者之前做出一些改变。例如,我们有 Observable 的 Int,它发出了 2,3,4 三个数字,现在我们想要它们在传给订阅者之前都乘以 10,我们可以这么做:

image.png

Observable<Int>.of(2, 3, 4).map { value in
        return value * 10
    }.subscribe(onNext: {
        print($0)
    }).disposed(by: disposeBag)
// 输出:20 30 40

Filter:

您可能又会想是否能让它们在传给订阅者之前过滤掉一些值,例如,过滤掉示例中大于 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

FlatMap:

又比如您有两个 Observable 对象,并且您希望将它们合二为一:
image.png

在上面的例子中,Observable A 和 Observable B 被组合在一起并形成一个新的 Observable:
let sequenceA = Observable.of(1, 2)
let sequenceB = Observable.of(1, 2)
let sequenceOfAB = Observable.of(sequenceA, sequenceB)
sequenceOfAB.flatMap { return $0 }.subscribe(onNext: {

print($0)

}).disposed(by: disposeBag)

##distinctUntilChanged 或 debounce:
这两个方法是搜索中最有用的方法之一。例如,用户想要搜索单词,您可能在用户输入每个字符时都调用搜索 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 的概念。


----

作者:iWeslie
链接:https://juejin.im/post/5cb833abf265da03ac0d04d1
来源:掘金
目录
相关文章
|
前端开发 Java 程序员
iOS开发 - 抛开表面看本质之iOS常用架构(MVC,MVP,MVVM)
iOS开发 - 抛开表面看本质之iOS常用架构(MVC,MVP,MVVM)
493 0
|
前端开发 测试技术 iOS开发
|
前端开发 iOS开发
iOS - MVVM 架构模式
1、MVVM 从字面意思来理解,MVVM 即 Modal View ViewModel(模型 视图 视图模型)。MVC 是一个用来组织代码的权威范式,也是构建 iOS App 的标准模式。Apple 甚至是这么说的。
1880 0
|
存储 缓存 前端开发
|
存储 前端开发 数据安全/隐私保护
|
4天前
|
JavaScript 搜索推荐 Android开发
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
23 8
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
|
1月前
|
iOS开发 开发者
uniapp开发ios打包Error code = -5000 Error message: Error: certificate file(p12) import failed!报错问题如何解决
uniapp开发ios打包Error code = -5000 Error message: Error: certificate file(p12) import failed!报错问题如何解决
143 67
uniapp开发ios打包Error code = -5000 Error message: Error: certificate file(p12) import failed!报错问题如何解决

热门文章

最新文章

  • 1
    【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
  • 2
    【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
  • 3
    Cellebrite UFED 4PC 7.71 (Windows) - Android 和 iOS 移动设备取证软件
  • 4
    【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
  • 5
    【01】噩梦终结flutter配安卓android鸿蒙harmonyOS 以及next调试环境配鸿蒙和ios真机调试环境-flutter项目安卓环境配置-gradle-agp-ndkVersion模拟器运行真机测试环境-本地环境搭建-如何快速搭建android本地运行环境-优雅草卓伊凡-很多人在这步就被难倒了
  • 6
    iOS8 中无需开源库的内置功能一览
  • 7
    iOS7应用开发7:自定义视图、手势操作
  • 8
    IOS小工具以及精彩的博客
  • 9
    Facebook SDK(iOS)初学讲解
  • 10
    iOS - Swift NSPoint 位置
  • 1
    【01】噩梦终结flutter配安卓android鸿蒙harmonyOS 以及next调试环境配鸿蒙和ios真机调试环境-flutter项目安卓环境配置-gradle-agp-ndkVersion模拟器运行真机测试环境-本地环境搭建-如何快速搭建android本地运行环境-优雅草卓伊凡-很多人在这步就被难倒了
    14
  • 2
    Cellebrite UFED 4PC 7.71 (Windows) - Android 和 iOS 移动设备取证软件
    28
  • 3
    【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
    34
  • 4
    【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
    29
  • 5
    【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
    23
  • 6
    uniapp开发ios打包Error code = -5000 Error message: Error: certificate file(p12) import failed!报错问题如何解决
    143
  • 7
    【05】2025年1月首发完整版-篇幅较长-苹果app如何上架到app store完整流程·不借助第三方上架工具的情况下无需花钱但需仔细学习-优雅草央千澈详解关于APP签名以及分发-们最关心的一篇来了-IOS上架app
    235
  • 8
    app开发之安卓Android+苹果ios打包所有权限对应解释列表【长期更新】-以及默认打包自动添加权限列表和简化后的基本打包权限列表以uniapp为例-优雅草央千澈
    90
  • 9
    深入探索iOS开发中的SwiftUI框架
    145
  • 10
    ios样式开关按钮jQuery插件
    60