章节1: Hello RxSwift!

这本书旨在介绍RxSwift库和用Swift编写响应式iOS应用。
但是RxSwift到底是什么?这儿有个好很形象的定义:

RxSwift是一个异步编码,通过观察序列和功能设计操作,允许调度者参数化执行的基于事件编码的库

听起来复杂吗?不要担心。编写响应式程序,理解背后的许多概念,查询许多相关的常用术语或许令人生畏 — 特别是当你还没有详细学习的时候想把它一次掌握。

这就是这本书的目标:通过如何使用每个api,然后在iOS应用中学习实际用法来逐步介绍各种RxSwift API和RX的概念。

你将开始了解RxSwift的基本特征,然后逐渐过渡到中级和高级的部分。花时间理解抽象的新概念,这个提升会让你更容易掌握这本书的结尾部分。Rx这本书里覆盖了太宽泛的主题;相反,我们的目标是让你充分的理解这个库,这样您就可以发展自己的Rx技巧。

但是我们还不完全确定RxSwift是什么,是吗?在这一章之后让我们先从一个简单的,可以理解的定义和一个更好的、更富有表现力的响应式编程开始。

RxSwift在其本质上,简化了开发异步程序通过允许你的代码按顺序的响应,处理,分离。

作为一个iOS开发,相比于本章节早期我们应该更清楚和更多的了解到RxSwift是什么。

即使你仍然对细节比较模糊,我们应该清楚RxSwift帮助我们编写异步代码。我们知道开发良好的、确定的、异步的代码是很困难的,所以任何帮助是相当受欢迎的!

介绍异步编程

如果你想用语言解释异步编程,你可能提出以下东西。
在一个iOS app中,在任何时刻,都可能做很多不同的事情:

  • 响应按钮点击
  • textField失去焦点键盘动画
  • 从网络下载大图片
  • 保存大数据到磁盘
  • 播放音乐
  • 其他

所有的事情看起来都发生在同一时间,尽管键盘移动到屏幕之外,当动画结束后音乐还没有暂停,对吗?

章节1: Hello RxSwift!_第1张图片
426A7837-E020-4D38-AD61-F668327F147B.png

所有不同的程序不阻止对方的执行。iOS提供给你各种api允许你执行不同的工作在不同的线程,不同的核心在设备的CPU中。

编写真正的并行运行代码相当复杂,特别是当不同的代码需要使用相同的数据。很难争论哪段代码先更新数据,或者晚读取数据。

Cocoa and UIKit Asynchronous APIs

苹果在iOS SDK中提供了大量的api来帮助你编写异步代码。你在项目中使用这些,可能没有想过太多因为他们编写app是如此的基础。

我们可能用的最多的如下:

  • NotificationCenter: 在任何时间执行一段代码当关注的事件发生时,如用户改变设备的方向,或者软件键盘在屏幕上显示或隐藏。
  • The delegate pattern:定义一些方法和api在另一个类执行在任何时候。例如,当通知到达时在application delegate里定义事件,但是你不知道这个事件什么时候发生,和发生几次。
  • Grand Central Dispatch:帮助你抽象的执行工作。您可以安排代码在一个串行顺序执行队列,或同时在不同的队列根据优先级运行大量的任务。
  • Closures:创建独立的代码闭包,您可以通过类与类之间决定是否执行,执行多少次,在什么情况下执行。

因为大多数典型的类会做异步,和所有UI组件在本质上是异步的,不可能做出假设全部程序代码会以什么顺序的执行。

毕竟,你的程序代码根据不同的外部因素运行不同,如用户输入,网络活动或其他操作系统事件。每次用户启动应用程序,代码可能运行在一个完全不同的顺序取决于这些外部因素。(好吧,除了当你机器性的测试你的应用程序,然后你可以期望所有事件按预期发生,在同步杀死)。

我们不是说编写好的异步代码是不可能的。毕竟,从苹果上面列出api,非常先进,非常专业,公平地说,相比其他平台提供的更强大。

问题是复杂的异步代码变得很难写,部分原因在于苹果的SDK提供的各种api:

章节1: Hello RxSwift!_第2张图片
2.png

使用代理时,您需要采用一个模式,另一个为闭包,另一个方法订阅NotificationCenter,等等。由于没有统一的语言贯穿异步api,阅读和理解代码,对其执行和推理,变得困难。

结束本节,将讨论到更多的上下文,你将比较两段代码:一个同步,一个异步的。

同步代码

演示一个操作,遍历数组你可能已经做了很多次。这是一个非常简单而可靠的应用程序逻辑构建块,因为它保证两件事:它同步执行,集合遍历它时是不可变的。

花点时间想想这意味着什么。遍历一个集合时,您不需要检查所有的元素是否仍然存在,你不需要倒回到另一个线程在这个集合中插入一个元素。你总是以为在循环的开始你就是在遍历集合全部。

如果你想用for循环来演示更多这方面的,在后台线程试试这个:

var array = [1,2,3]
      for number in array {
          print(number)
          array = [4,5,6]
      }
      print(array)
------------------------------------------
输出:
1
2
3
[4, 5, 6]

在for循环里数组可变吗?循环里的每个元素是否改变?执行的所有命令的顺序是什么?如果需要你可以修改元素吗?

异步代码

考虑类似的代码,假设每点击一次按钮遍历一个元素。用户反复点击按钮,程序输出数组中的下一个元素:

 var array = [1,2,3]
    var currentIndex = 0
    @IBAction func printNext(_ sender: UIButton) {
        print(array[currentIndex])
        
        if currentIndex != array.count-1 {
            currentIndex += 1
        }
        
    }
-----------------------------------------------------------------------
输出:
1
2
3
3
3

想想这段代码和上一段。当用户点击按钮时,会打印数组的所有元素吗?你真的不能说。另一块异步代码在打印之前可能删除最后一个元素。或另一段代码可能会在你移动之后插入一个新元素。

当然,如果你在printNext(_:)函数中不改变currentIndex,但是另一个代码段可能修改currentIndex — 也许在某个点添加一些聪明的代码来达到上面函数的功能。

你可能意识到编写异步代码的一些核心问题,是:a)每段代码执行的顺序 b)共享可变数据。

幸运的是这些都是RxSwift的优点!

接下来,你需要一个好的语言帮助您开始了解RxSwift是如何工作的,它解决了什么问题,在下一章最终让你越过初步介绍和编写你的第一段Rx代码。

异步编程术语

RxSwift的一些语法是紧密地绑定到异步的,响应式,和函数式编程,这是很容易的,你首先了解以下基本条件。

一般来说,RxSwift试图解决以下问题:

1. 状态,特别是共享可变状态
状态是很难定义。要理解状态,考虑下面的实例。当你开始你的笔记本电脑运行得很好,但是在你使用它几天甚至几周,它可能开始表现得古怪或突然挂掉。硬件和软件是一样的,但是改变的是状态。当你重启,相同的硬件和软件的组合将会工作的很好。数据在内存中,存储在磁盘上,对用户的输入做出反应,所有工件的所有痕迹,从云服务获取数据后——这些就是笔记本的状态。管理你的应用程序的状态,尤其是当多个异步组件之间共享,您在这本书中将了解如何处理这个问题。

2. 命令式编程
命令式编程是一种编程范式,使用语句来改变程序的状态。就像你会使用命令式语言当你玩你的狗的时候——“取回!躺下!玩死了!”——你使用必要的代码告诉应用程序何时以及如何做事。必要的代码类似于你的计算机理解的代码。所有的CPU是遵循冗长的简单的指令序列。人们编写复杂的代码应用程序——特别是当共享可变状态是变得具有挑战性。例如这个代码,控制器中的viewDidAppear(_:)

override func viewDidAppear(_ animated: Bool) {
  super.viewDidAppear(animated)
  setupUI()
  connectUIControls()
  createDataSource()
  listenForChanges()
}

没有告诉这些方法做什么。他们更新控制器本身的一些属性?更令人不安的是,他们被称为正确的顺序?也许有人无意中交换这些方法调用的顺序并提交源代码。现在的程序可能因为交换变得不同。

3. Side effects
现在你知道更多关于可变状态和命令式编程,你可以确定大多数问题与这两件事的副作用。副作用是当前范围以外的任何改变状态。例如,考虑最后一段代码在上面的例子中。connectUIControls()可能高度封装某种事件处理程序的UI组件。这会引起副作用,因为当视图状态变化时程序在执行connectUIControls()之后会变得不同。任何时候你修改存储在磁盘上的数据或更新在屏幕上的文本标签,你会产生副作用。副作用本身并不是坏事。毕竟,导致副作用是任何程序的最终目标!你需要改变状态后,程序执行完成。运行一段时间,什么也不做是相当无用的应用。产生副作用的问题是控制的方式。您需要能够确定这段代码导致副作用,和简单流程和输出数据。RxSwift试图解决上述问题(或问题)通过解决以下两个概念。

4. Declarative code
在命令式编程中改变状态时。在功能代码中你不可能一点副作用也不产生。既然你没有生活在一个完美的世界,就会采取中间的平衡。RxSwift结合必要的一些命令式代码和功能代码。声明代码允许您定义的行为,RxSwift将运行这些行为在任何时间并给他们提供一个不可变的,独立的数据输入。用这种方法,你可以使用异步代码,但同样的假设在一个简单的for循环:你使用不可变的数据,你可以按顺序执行代码,确定的方法。

5. Reactive systems
系统响应是一个相当抽象的概念,覆盖web或iOS应用程序大多数特征如下:

  • Responsive 总是保持UI更新,最新的应用程序状态。
  • Resilient 每个行为定义成孤立的和提供了灵活的错误复苏。
  • Elastic 一段处理不同的工作代码,通常实现特性,比如懒惰pull-driven数据收集,事件节流和资源共享。
  • Message driven 组件使用基于消息的通信来提高可重用性和孤立,解耦的生命周期和实现类。

现在您已经很好地理解RxSwift能够帮助解决的问题,以及它如何解决这些问题,是时候讨论创建Rx代码块和他们如何协调工作。

Foundation of RxSwift

响应式编程并不是一个新概念;它已经存在了很长时间,但在过去的十年里它的核心概念有了明显的回归。在那个时期,web应用程序变得更复杂,面临的问题管理复杂异步ui。在服务器端,响应式编程(如上所述)已成为必须。微软团队提出了解决异步的问题的挑战,可伸缩的、实时的应用程序开发,我们在本章中讨论。他们在一个库中,独立于公司的核心团队,2009年左右,提供了一个新的客户端和服务器端框架称为。Reactive Extensions for .NET (Rx).。这是一个用于.NET 3.5上的可安装的插件。后来在.net4.0中编译成一个核心库。2012年变成开源组件。开源的代码允许其他语言和平台重新实现相同的功能,Rx变成一个跨平台的标准。今天有 RxJS, RxKotlin, Rx.NET, RxScala, RxSwift,等等。所有这些库努力实现相同的行为和相同的api。最终,RxSwift开发iOS程序的可以和RxJS开发web的人一起讨论逻辑。像原生的Rx,RxSwift也适用于所有的概念到目前为止,它解决可变状态,它允许您编写事件序列等架构概念,提高代码隔离,可重用性,解耦。

让我们重新定义:

RxSwift发现传命令代码和功能代码的优点。它允许通过不可变得代码定义来响应事件处理异步的输入。

你可以阅读更多关于Rx的实现http://reactivex.io。这是关于Rx的运营商和核心课程的仓库文档。也可能首先你会发现Rx标志,电鳗(你会在这本书的封面上发现一个更逼真的图像):

在本书中,你将涵盖的基础概念的发展和RxSwift实际开发例子和如何在应用程序中使用它们。Rx的三个模块是观察者,操作和调度器。下面的章节涵盖每一个细节。

Observables
Observable类提供了Rx代码的基础:简单来说,异步能力产生了捕获不可变数据快照的事件序列,它允许一个类来订阅另一个类的值

Observable< T >类允许一个或多个观察者实时响应事件和更新程序的UI,或者处理新的输入数据。

ObservableType协议非常简单。一个者的只能发出(和观察员可以接收)三种类型的事件:

  • A next event:一个捕获最新值(或下一个值)的事件,观察者接收值
  • A completed event:这个事件成功终止事件序列。这意味着观察者成功完成其生命周期,不会发出任何其他事件。
  • Anerrorevent:观察者以错误终止并且不会发出其他事件。

当谈到异步事件发送超时时,你可能形象成一条时间线上的观察者是一串数字,像这样:

章节1: Hello RxSwift!_第3张图片
timeOver.png

在Rx中这三种可能事件观察者可以发出任意一种,因为它是如此普遍,你甚至可以使用它来创建最复杂的应用程序逻辑。

因为订阅者不会预测观察者的任何特性,使用事件流是最终的解耦实践。你不需要使用委托协议,或注入闭包允许类之间交互。

章节1: Hello RxSwift!_第4张图片
438C08C1-1A6C-494C-B6F8-EA18F2BE0F01.png

为了得到一个真实情景的猜想,你会看到两种不同的可观察序列:

Finite observable sequences

一些观察序列发送零个、一个或多个值,最后,成功解除或终止错误。

在iOS程序中,思考从网络中下载文件的代码:

  • 首先,你开始下载,开始观察输入数据。
  • 然后你多次收到大量文件的数据部分。
  • 如果网络失去连接,下载将停止和连接超时。
  • 另外,如果下载完所有文件数据,成功结束。

这工作流程准确地描述了一个典型的可观测的生命周期。看一看相关代码如下:

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实例,你可以提供onNext闭包来订阅next事件,在下载实例中,你将数据附加到一个临时文件存储在磁盘上。你可以提供onError闭包来订阅error事件,在闭包中你可以展示错误信息和提示框或其他操作。最后,提供onCompleted闭包来处理completed事件,可以跳转新控制器或展示下载文件大小或其他操作。

Infinite observable sequences

与文件下载或类似的活动,应该是自然或强制终止,针对这些无限序列。通常,UI事件真是无限可观察序列。例如,你需要处理设备方向的改变:

  • 你需要为你的类添加UIDeviceOrientationDidChange的通知
  • 你需要提供一个回调方法来处理方向变化。它根据最新方向做出反应。

方向改变的序列变化没有结束。只要有设备,有一个可能的方向变化。进一步,因为几乎是无限的,你总是有一个初始值时你开始观察它。也可能用户永远不旋转它,但是这不意味着结束,这仅仅是没有时间发出。在RxSwift中,你可以这样写代码来处理方向变化:

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是一个产生Observable的虚构的控制属性,订阅和根据当前方向更新app UI,省略了onError 和 onCompleted参数,因为这种事件永远不会发生

Operators

ObservableType和Observable类的实现包含了大量的方法和抽象的独立的异步代码段,可以结合完成更复杂的逻辑。因为它们高度解耦,这些方法通常归类为operators。因为这些操作主要在异步输入,只输出不会引起副作用,他们可以很容易地组合在一起,就像拼图,努力拼出一个更大的图片。

例如,把数学表达式(5 + 6)* 10 - 2。以一个清晰的、确定的方式,您可以应用操作符*,(),+和-在他们的预定义的命令的数据输入,得到输出和保持处理表达式直到解决。

以类似的方式,您可以应用Rx操作符的输入发出一个可观测的确定性过程的输入和输出,直到表达式已经解决了最后一个值,你可以使用引起的副作用。这是前面的例子关于观察方向变化,调整使用一些常见的Rx操作符:

UIDevice.rx.orientation
  .filter { value in
    return value != .landscape
  }
.map { _ in
    return "Portrait is the best!"
  }
  .subscribe(onNext: { string in
    showAlert(text: string)
  })

每次UIDevice.rx.orientation会产生横盘和竖屏,Rx将会根据发送的值过滤。

章节1: Hello RxSwift!_第5张图片
AEC03999-AACA-4C66-A091-11C6291B78BA.png

首先,过滤出不是横屏的。如果设备是横屏,subscribe代码段将不会执行因为过滤将会终止这个事件。如果是竖屏,map操作将会获取设备的方向然后转换成字符串输出"Portrait is the best!".最后,subscribe代码段捕获这段字符,调用方法展示弹框。

operators也高度可组合——他们总是以数据为输入和输出结果,所以你可以很容易的用许多不同的方式实现!通过这本书,您将了解更复杂的操作符抽象更复杂的异步工作。

Schedulers

Schedulers相当于调度队列 —— 只是更容易使用

RxSwift提供了一系列预定义的调度器,覆盖99%的用例,希望你永远不需要去创建自己的调度器。

事实上,本书的上半部分大多数的例子都很简单,一般处理观察数据和更新UI,所以你根本不会考虑调度器,直到你已经学会了基础知识。

那就是说,调度器是非常强大的。

例如,您可以在SerialDispatchQueueScheduler里观察next事件,它使用中央调度在给定的队列里按序列允许你的代码。

ConcurrentDispatchQueueScheduler并发运行您的代码。OperationQueueScheduler将允许您在给定NSOperationQueue安排你的订阅。

您可以给同一个订阅安排不同的工作来实现最佳的性能。

RxSwift将在订阅和调度之间充当调度者,正确派遣工作和无缝协调。

章节1: Hello RxSwift!_第6张图片
83D7E9EF-2178-448A-86A3-48EAF568CA07.png

这个图表中,带颜色的工作块通过不同的调度者被安排有序的工作,例如:

  • 蓝色的网络订阅块(1)基于调度器在custom NSOperation上运行一段代码。
  • 输出的数据这一块作为下一个块(2)的输入,运行在后台线程,一个不同的调度器中。
  • 最后,绿色块(3)运行在主线程用来更新UI

即使这看起来很有趣,非常方便,彼此不会干扰。本书中稍后会回到这节。

App architecture

值得一提的是,RxSwift不以任何方式改变应用程序的体系结构;它主要处理事件,异步数据序列,和通信交互。

您可以用RX创建app根据苹果开发者文档实现MVC架构。你也可以选择实现MVP架构或者MVVM。

如果你想,Rx也非常适合单向数据流架构。

注意,没必要从头开始一个项目,当你的APP有新特性时你可以灵活的,简单的用RXSwift。

微软的MVVM架构是专门为事件驱动的软件开发平台提供数据绑定。RxSwift 和MVVM能更好的结合,这本书末你会考虑用RxSwift如何实现它。

MVVM个RxSwift能更好的结合是因为ViewModel允许观察Observable 那些直接在控制器中绑定UIKit视图的属性,使得绑定模型数据到UI非常简单

章节1: Hello RxSwift!_第7张图片
31D11B6E-AFC0-4D6F-A190-4AA1C09E7F03.png

书中其他例子使用MVC架构是为了保持示例代码简单,容易理解。

RxCocoa
RxSwift是Rx API的通用实现,因此跟Cocoa或UIKit-specific类没有任何关联。

RxCocoa是专门为UIKit提供支持开发的RxSwift的协同库。一些更高级的特征,RxCocoa为UI组件添加了许多扩展,这样就可以订阅各种UI事件。

例如,可以很容易的订阅到UISwitch的状态改变,如下:

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

RxCocoa为UISwitch类添加了rx.isOn属性,所以你可以订阅到有用的时间序列。

章节1: Hello RxSwift!_第8张图片
w111.png

另外,RxCocoa为UITextField,URLSession,UIViewController添加了rx命名空间,还有更多。

安装RxSwift
RxSwift免费提供https://github.com/ReactiveX/RxSwift。

RxSwift是MIT许可下发布的,可以免费引用。与其他MIT开源软件一样,版权声明应包含在你的app里。

在RxSwift仓库中有很多实践,包含RxSwiftRxCocoa库,也有Rx的测试案例RxTestRxBlocking

除了所有的源代码(绝对值得窥视),你会发现Rx.playground,演示了许多的operators。也看看RxExample,这是一个很好的展示应用程序,演示了在实践中的许多概念。

最简单的方法引入RxSwift / RxCocoa在您的项目是通过CocoaPods或Carthage。您还可以使用包管理器。

在这本书中使用CocoaPods的项目。即使你通常使用不同的依赖管理器,学习本书中请使用CocoaPods。

在这本书开始之前,作者讨论了几种方法,他们决定使用CocoaPods学习RxSwift时更有用,因为你可以随时Cmd-click直接跳到它的源代码。在自己的项目中,你可以自由使用CocoaPods,Carthage,或另外的管理器
—— 更适合自己的。

注意:如果你想用Carthage,感觉自由;请注意这本书只包括CocoaPods说明。

RxSwift via CocoaPods
你可以像引入其他库一样引入RxSwift,在Podfile中输入:

use_frameworks!
target 'MyTargetName' do
  pod 'RxSwift', '~> 3.2'
  pod 'RxCocoa', '~> 3.2'
end

当然,你可以只引入RxSwift,或RxSwift和RxCocoa一起,或者直接在git仓库下载。

Installing RxSwift in the book projects
至于这本书的项目,他们都有一个Podfile文件,但没有包含依赖文件。我们看着这个选项,但是它没有包含RxSwift文件,所以我们在每个章节都需要单独下载。

在开始工作之前,确保您已经安装了最新版本的CocoaPods。你需要在终端执行一次这个:

 sudo gem install cocoapods

如果想知道更多, 参考CocoaPods官网:https://guides.cocoapods.org/using/getting-started.html.

在每章开始之前,我们都需要启动项目安装RxSwift,操作很简单:

1.在本书目录中找到工作的章节。
2.拷贝starter文件夹到你的用户文件夹。
3.打开终端,导航到starter文件夹,cd /users/yourname/path/to/starter,用真实路径替换案例路径。
4.输入podinstall从github上拉取RxSwift。
5.最后,打开创建的.xcworkspace文件。

RxSwift via Carthage
通过Carthage安装RxSwift是最新的。首先确保安装了最新版的Carthage, https://github.com/Carthage/Carthage#installing-carthage.

在项目中,创建一个新的文件夹命名为Cartfile,添加下面这一行:

 github "ReactiveX/RxSwift" ~> 3.0

接下来,在您的项目的文件夹执行carthage update.
这将去下载所以得库,可能花费一些时间,完成之后,在Carthage目录下创建了库文件,引入到项目中。
再次构建新添加的框架,以确保Xcode索引,准备好了。

Community

RxSwift项目是很活跃的,不仅仅是因为可以创建很酷的软件,也是因为有一个活跃的社区。RxSwift社区非常友好,开放,热衷于讨论常见的技术,互相帮助。除了官方RxSwift库,
你会发现大量的Rx自由爱好者:http://community.rxswift.org。
更多的Rx库和实例在这里:https://github.com/RxSwiftCommunity。
更多感兴趣的人:http://rxswift-slack.herokuapp.com
Slack频道有2500个会员!每天的主题就是彼此帮助和交流。

Where to go from here?

这一章向您介绍了许多RxSwift能够解决的问题。您了解了异步编程的复杂性,共享可变状态,造成副作用,和其他的东西。

你还没有写任何RxSwift实例,但是你现在明白为什么RxSwift是个好主意,你知道它解决的问题类型。这应该给你一个好的开始。

还有很多工作要做。我们要开始通过MVVM架构创建一个非常简单的observables案例APP。

请看第二章,“Observables”!

你可能感兴趣的:(章节1: Hello RxSwift!)