译自:Introducing RxSwift: Reactive Programming with Swift!

简介

“If you’ve ever used an asynchronous callback based API, you’ve probably dealt with handling the response data ad-hoc all across your codebase, and have most likely decided there was no way to unit test it all… But, let me tell you - there is a better way, and it’s called Rx!”
— Krunoslav Zaher, creator of RxSwift

不可否认,Rx是当今移动应用开发中最热门的话题之一,它是一个多平台的标准,基本上前卫一点的开发者都会有所接触,所以无论是网络开发大会,还是Android,或者Swift研讨会,基本上每次沙龙或者线下面基都会必现他的身影。
对于RxSwift来说,只是其中的一个系列,让你可以使用全新的方式在你敲喜欢的(???)的Swift语言下更容易的编写和测试的优雅代码。
要开发出一个强大高效的应用,肯定就要处理多个并发任务,如播放音频,处理用户界面输入,进行联网调用等等。有时候,将数据从一个进程传递到另一个进程,或者甚至只是观察这些任务以不同的顺序依次发生,仅仅是这些需求,常规处理,就会开发时间直线上升。
本次学习中,我们将会针对RxSwift如何解决与异步编程有关的问题进行讲解,并掌握它,我们从观察简单的数据序列到组合和转换异步流,从而设计架构和构建高质量的应用。

学习成本

  • 至少需要安装OS X El Capitan系统的Mac。
  • 至少安装Xcode并且版本需要在8.0以上。
  • 需要对iOS开发有一定的经验(怎么说也得一年以上开发经验吧!!!),SwiftUIKit有较好的理解。
  • 本教程不需要开发者账号(2016苹果开放了真机调试权限,Apple ID还是需要的)。

本次学习是针对已经习惯使用Swift的iOS开发者,并希望深入研究RxSwift。如果你是一个iOS初学者,建议还是先把Swift和iOS基础夯实了再来进阶。

本次学习会在每个章节都提供了适合对应的项目和源代码,并且涵盖了少量的编程技巧,有些章节设计纯理论,可以自行使用Playground进行上手。如果读者对于RxSwift有一定的基础了解,不放也阔以先阅读以下前面的基础章节,说不定有啥意想不到的收获。

章节预览

  • RxSwift入门:第一部分涵盖了RxSwift基础知识。不要跳过这一节,因为在下面的章节中,我们将需要很好地理解它的工作方式和原理。
  • 操作符和最佳实践:在本节中,一旦掌握了基本知识,就可以通过使用操作符来构建更复杂的Rx代码。操作符允许你链接和编写一小部分功能来构建复杂的逻辑。
  • iOS应用与RxCocoa:一旦你掌握了RxSwift的基础知识并且知道如何使用操作符,你就可以用RxSwift代码封装现有的iOS类和UI控件。
  • 中级RxSwift / RxCocoa:在本节中,我们将谈到更多的话题,比如为应用构建错误处理策略,处理网络需求,响应方式,编写Rx测试等等。
  • RxSwift扩展:许多基于RxSwift的可用库都是由社区创建和维护的。在本节中,我们将介绍一些这些项目以及如何在自己的应用中使用它们。
  • 融会贯通:这一部你将学习如何构建你的项目,并探索几种不同的方法来设计数据流和项目导航。

Hello RxSwift

RxSwift是什么,RxSwift是一个库,用于通过使用可观察序列和操作符来组合基于事件的异步代码,从而优化函数体的调度参数。

听起来很复杂是吗?Don’t Worry!!!编写响应式程序,需要我们理解其背后的许多概念,以及浏览大量相关的常用术语,这肯定会让人感到恐惧,所谓万事开头难,你自己克服下囖。
本次学习我们会一次介绍各个API及其用法,并在实际的例子和项目中进行实战,让我们可以更好的理解RxSwift的API以及Rx的编程概念。

本次学习是RxSwift主题,奉劝如果脑子里还没有异步的概念,还是就此打住吧,后面内容挺多,你吃不消的。

基础概念

  • 状态,可变状态:状态这个词很难定界,各人理解不同,一个通俗的例子就是,比如你的PC,开机的时候都多正常的,Windows跑上三五天,Mac跑上几个月,就会出现很多怪异的问题,装机前辈告知的都是电脑有问题先重启,不行再重装,Windows尽量不要想着修复,耗上三五天只会越修复越奇怪。在软硬件不变的情况下,这里面的问题就是系统状态出了问题,有可能是内存,硬盘或者日志的一些东西导致,尤其是系统状态是多应用之间共享的可变状态。
  • 命令式编程:它就和面向过程的C一样,没有方法,只有函数。你只需要把你各自的小功能封装成函数,调用函数执行即可。函数就想命令,比如你让你家的汪,躺下,跳起来一样。。。
    比如这样:
    1
    2
    3
    4
    5
    6
    7
    override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    setupUI()
    connectUIControls()
    createDataSource()
    listenForChanges()
    }
  • 副作用:涉及到状态这种情况就会有副作用的产,比如有一个全局变量,很多地方都在改变其状态,那就不仅有作用,一定还有很多隐性bug的产生,这里就不多缀诉。
  • 声明代码:在命令式编程中,你可以随意更改状态而不会造成任何副作用。RxSwift结合了命令代码和功能代码最好的方面来为你保驾护航。
  • 响应式系统:让UI组件根据不同的应用状态保持最新显示。组件基于消息的通信来改进,高可重用性和隔离性,将类的生命周期和实现分开,更易测试。

RxSwift的基础

响应式编程不是一个新概念,它已经存在了相当长的一段时间,但是其核心概念在过去的十年中已经引起了强势回归。
在此期间,Web应用已经变得越来越复杂,并且正面临着管理复杂的异步UI问题。在服务器端,响应式系统已经势在必行。
Rx是微软团队在2009年左右,提供了一个名为Reactive Extensions for .NET(Rx)的新客户端和服务器端框架。
它是.NET 3.5的可安装插件,后来成为.NET 4.0中的一个内置的核心库。自2012年以来,它一直是一个开源组件。开源代码允许其他语言和平台重新实现相同的功能,从而使Rx成为跨平台的标准。
而有今天的RxJS,RxJava,RxKotlin,Rx.NET,RxScala,RxSwift等等。这些库都在努力实现相同的行为和相同的表达API。最终,通过使用RxSwift创建iOS应用的开发也可以在Web上使用RxJS与另一位程序狗自由讨论应用逻辑问题。
和最初的Rx一样,RxSwift也适用于迄今为止所涉及的所有概念:它处理可变状态,它允许编写事件序列并改进体系结构概念,例如代码隔离,可重用性和解耦。

更多关于Rx的问题可以访问他们主页:http://reactivex.io/

废话了一大堆,终于可以进入主题,让我们开始observablesoperatorsschedulers的学习。

Observables

Observable是Rx的基础,它是泛型定义Observable<T>,我们把它理解成一个被观察对象,它所发出的异步数据流是根据泛型T决定。Observable允许一个或多个观察者订阅,来响应事件进行逻辑并更新UI,或者处理新传入的数据等。
Observable符合ObservableType协议,它使Observable可以发射(而观察者可以接收)三种类型的事件:

  • next事件:此事件会携带最新值以便提供给订阅者使用。
  • completed事件:该事件表示成功结束事件序列。这意味着Observable成功完成了它的生命周期,从此不会再发生任何其他事件。
  • error事件:Observable携带出错信息终止序列,从此不会再发出其他事件。

异步事件是随着时间推移而转变,比如下面这个可观察的Int类型序列:

又比如这个Int序列有三个观察者,辣么三个观察者都会受到此序列发出的相同的事件:

实际开发中,大致分为两种序列,有限序列和无限序列:

有限的observable序列

它可以发出零个,一个或者多个值,在最后以成功或者错误而终结。
比如iOS开发里常见的文件下载:

  • 首先,开始下载并开始观察传入数据。
  • 然后在文件的一部分进来时,您重复接收大量的数据。
  • 如果网络连接断开,则下载将停止,并且连接将超时且出现错误。
  • 或者,下载完所有文件的数据,它将会成功完成。

这个流程描述了一个典型的observable生命周期,代码如下:

1
2
3
4
5
6
7
8
9
10
API.download(file: "http://www...")
.subscribe(onNext: { data in
... append data to temporary file
},
onError: { error in
... display error to user
},
onCompleted: {
... use downloaded file
})

API.download(file:)通过返回一个Observable<Data>实例,因为网络上的数据传输都是二进制数据,所以这里采用Data类型。

  • 在这个下载的例子中,我们通过订阅onNext事件,将数据缓存到临时文件夹中。
  • 通过订阅onError事件在警告框中显示error.localizedDescription或执行其他操作。
  • 最后,订阅onCompleted事件,在这里可以push一个新的视图控制器来显示下载的文件或者其他内容。

无限的observable序列

说道无限序列,这就会扯到UI事件了,理想状态下它们是在一个runloop里一直无限循环等待触发。比如设备方向转换,屏幕点击,传感器触发等事件。
让我们看看设备方向的示例:

尽管有的屌毛用户会把设备方向锁死,但是这并不影响事件的无限性。我们来看看下面处理设备方向的逻辑:

1
2
3
4
5
6
7
8
9
UIDevice.rx.orientation
.subscribe(onNext: { current in
switch current {
case .landscape:
... re-arrange UI for landscape
case .portrait:
... re-arrange UI for portrait
}
})

这里的UIDevice.rx.orientation是Rx针对UIKit的一个扩展属性,它会提供一个Observable<Orientation>的可观察序列,我们订阅它并根据当前的方向更新UI。我们完全可以跳过onErroronCompleted事件,因为这些事件永远不会从这个可观察的事件中发出。

Operators

Rx同流行的函数式编程一样,提供了很多基础的函数,我们只用关心输入和输出即可,一个数据流输如,输出另一个新的数据流:

1
2
3
4
5
6
7
8
9
10
UIDevice.rx.orientation
.filter { value in
return value != .landscape
}
.map { _ in
return "Portrait is the best!"
}
.subscribe(onNext: { string in
showAlert(text: string)
})

看代码一脸懵逼是吗?看看流程图:

  • 首先,过滤器只允许不是.landscape的值通过,如果设备处于横向模式,则订阅的代码将不会被执行,因为过滤器(filter)已经过滤掉这些事件。
  • .portrait的情况下,我们通过映射器(map)转换成“竖屏体验最好”的描述。
  • 最后我们通过订阅事件流得到一个String值用于警告框的内容显示。

filtermap只是最基础最基础的操作函数,接下来我们将一步步介绍跟多复杂的操作函数,以便让我们在更复杂的需求里能更得心应手的组合使用。

Schedulers

Schedulers是Rx的调度器,它是用于任务队列调度。RxSwift附带了许多预定义的调度器,涵盖了99%的用例,所以不到万不得已你还是不要手痒自己去创建。
本次学习前面大部分的示例都是很简单的,通常都是在处理观察数据和更新UI,因此前期不要太过担心调度器的学习成本。这里我们简单的介绍下有个印象就可以了,后面接触到的时候再实地的学习。

我们只需要理解到此图表达的意思就是在以NSOperation的自定义调度器上拉取数据,在后台并发调度器上解析数据,最后在主线程调度器上更新UI。

应用架构

这里非常值得一提的就是,我们并不用担心因为引入RxSwift而破坏原本架构,因为我们的Rx只是处理事件和异步数据序列。你可以继续使用MVC架构,亦或是MVPMVVM都可以,随你喜欢。甚至你还可以选择实现自己的单向数据流架构也都中。

微软的MVVM架构是专门为在提供数据绑定的平台上创建的事件驱动软件开发的。 RxSwift和MVVM是一对好基友,在本次学习的最后我们会结合两者完成实战,以及如何使用RxSwift来实现它。
MVVM和RxSwift走得很好的原因是,ViewModel允许你公开Observable<T>属性,这样你便能使用少量的胶水代码直接在ViewController中绑定好UIKit空间。这使得绑定模型数据到UI非常简单:

本次学习的大部分示例都会以MVC模式编写,防止引入MVVM带来的学习成本和恐惧,更以便示例代码简单易懂。

RxSwift是Swift版本的Rx的通用实现。因此,它和Cocoa,UIKit两者没有任何关系。
RxCocoa库是RxSwift的小伙伴,它是专门为UIKit和Cocoa开发的库。 除了一些高级类,RxCocoa还为许多UI组件添加响应式扩展,以便您可以订阅各种UI事件。
例如,使用RxCocoa来订阅UISwitch的状态变化非常简单,如下所示:

1
2
3
4
toggleSwitch.rx.isOn
.subscribe(onNext: { enabled in
print( enabled ? "it's ON" : "it's OFF" )
})

我们订阅开关状态以便处理其他逻辑。

类似的还有UITextField, URLSession, UIViewController等等。。。

安装RxSwift

首见官方免费版本:https://github.com/ReactiveX/RxSwift
作为iOS开发,使用CocoaPods或者Carthage随你喜欢,详情参考官方文档即可。