RxSwift I:开始学习 RxSwift

Rx_Logo_M.png

第一章:RxSwift 介绍

RxSwift 是一个组合异步和事件驱动编程的库,通过使用可观察序列和功能样式运算符来,从而允许通过调度程序进行参数化执行。

RxSwift 在本质上简化了开发异步程序,允许代码对新数据作出反应,并以顺序和孤立的方式处理它。

介绍异步编程

51526546882_.pic.jpg

使用 Cocoa 和 UIKit 异步的 API 的问题在于:复杂的异步代码变得非常难写,部分原因是苹果 SDK 提供的 API 种类繁多。

异步编程词汇表:

  1. 状态(State),具体地说,共享可变状态
  2. 命令式程序设计
  3. 副作用
  4. 宣告式編程:允许你定义行为片断, RxSwift 会在有相关事件的时候运行这些行为,并为它们提供一个不可变的,孤立的数据输入。
  5. 响应式系统(Reactive systems)

反应式系统是一个相当抽象的术语,它涵盖了 Web 或 iOS 应用程序,它们显示了大多数或全部以下特性:

  • 反应(Responsive):始终保持 UI 更新,代表了最新的应用程序状态。
  • 能復原的(Resilient):每个行为被隔离开来,并提供可恢复的错误恢复。
  • 灵活的(Elastic): 该代码处理不同的工作负载,通常实现诸如懒惰驱动数据收集、事件节流和资源共享等特性。
  • 消息驱动(Message driven):组件使用基于消息的通信来提高可重用性和隔离性,解耦生命周期和类的实现。

RxSwift 基础

61526549641_.pic_hd.jpg

这个标志是一只电鳗。(Rx 项目曾经被称为 Volta)

RxSwift 是微软开源的 ReactiveX 的 Swift 语言的实现。

RxSwift 在传统的 Cocoa 编程和纯函数编程之间找到了最佳位置。它允许您对事件作出反应, 方法是使用不可变的代码定义以确定性的、可组合的方式处理异步输入部分。

Rx 代码的三个组成部分是 observables, operators 和 schedulers

Observable类提供了 Rx 代码的基础:异步产生一系列事件的能力,它可以“携带”数据的不可变快照。简单来说,它允许类在一段时间内订阅其他类发出的值。

ObservableType 协议 (Observable需要符合的) 非常简单。可观测的可能发出 (并且观察员能接受) 仅三类型事件:

- next 下一个事件: “携带” 最新 (或 “下一个 “) 数据值的事件。这是观察者 “接收” 值的方式。

- completed 已完成的事件: 此事件以成功终止事件序列。这意味着可观察的完成其生命周期成功, 不会发出任何其他事件。

- error 错误事件: 可观察的终止带有错误, 不会发出其他事件.

两种不同的可观测序列: finite 和 infinite。

由于它们是高度解耦和可组合的, 所以这些方法通常称为运算符。比如 filter。

运算符也是高度可组合的,它们总是把数据作为输入并输出它们的结果,所以你可以用许多不同的方式轻松地将它们链接起来,实现比单个操作员自己能做的更多的事情。

调度程序是 dispatch queues 的 Rx 等价物。

RxSwift 将充当你的订阅(在左手边下面)和调度器(在右手边)之间的调度器,将工件发送到正确的上下文,并无缝地允许它们与彼此的输出一起工作。

71526551674_.pic_hd.jpg

要读取此关系图, 请在不同的计划程序中按预定的顺序 (1、2、3、…) 来执行彩色作品。例如:

·蓝色网络订阅以在基于自定义 NSOperation 的计划程序上运行的一段代码 (1) 开始。

·数据输出块作为下一个块 (2) 的输入, 它运行在一个不同的调度程序上, 它位于并发后台 GCD 队列中。

·最后, 在主线程调度程序上计划最后一块蓝色代码 (3), 以便用新数据更新 UI。

App architecture 应用的架构

值得一提的是,RxSwift 并没有以任何方式改变应用程序的架构;它主要处理事件、异步数据序列和通用通信协议。

通过在苹果开发文档中实现 MVC 体系结构,可以创建具有 Rx 的应用程序。如果你喜欢的话,你也可以选择实现 MVP 架构或 MVVM。RxSwift 也可以帮你实现自己的单向数据架构。

微软的 MVVM 架构是专门针对在平台上创建的事件驱动软件开发的,该平台提供数据绑定。RxSwift 和 MVVM 很好地结合在一起,在这本书的末尾,你会看到这个模式以及如何用 RxSwift 来实现它。

MVVM 和 RxSwift 结合在一起的原因是,ViewModel 允许您公开可观察的(Observable)属性,这些属性可以直接绑定到 View Controller 代码中的 UIKIT 控件。这使得绑定模型数据到 UI 非常简单地表示和编码:

81526552550_.pic.jpg

本书中的所有其他示例都使用 MVC 架构来保持示例代码简单易懂。

RxCocoa

RxSwift 是通用 Rx API 的实现。因此,它不涉及任何 Cocoa 或 UIKit 类。

RxCocoa 是 RxSwift 的同伴库,所有的类都有助于 UIKit 和 Cocoa 的开发。除了具有一些高级类之外,RxCocoa 还为许多 UI 组件添加了响应式扩展,以便您可以订阅不同的 UI 事件。

例如,使用 RxCocoa 订阅 UISwitch 的状态变化是非常容易的,例如:

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

RxCocoa adds the rx.isOn property (among others) to the UISwitch class so you can subscribe to generally useful event sequences.

RxCocoa 将 rx.isOn 属性(其中之一)添加到 UISwitch 类,这样您就可以订阅通常有用的事件序列。

101526552877_.pic.jpg

安装

官方 git:https://github.com/ReactiveX/RxSwift

使用 CocoaPods 或者 Carthage 均很方便集成 RxSwift。

社区

RxSwift 社区非常友好,思想开放,并且热衷于讨论模式,常用技巧或互相帮助。

更多的 Rx 库和实验,像雨后春笋一样的涌现,可以在这里找到:https://github.com/RxSwiftCommunity

可能最好的方式来满足许多对 RxSwift 感兴趣的人,这是 Slack 的频道:http://rxswift-slack.herokuapp.com

Slack 频道拥有约 5000 名成员! 日常的主题包括:互相帮助,讨论 RxSwift 或其同伴库的潜在新功能,以及共享 RxSwift 博客文章和会议讲座。

第二章:Observables

Observable 是什么

Observable、observable sequence 和 sequence 在 Rx 都是一个意思。或者在其他 Rx 实现中称之为 stream。

最好称之为 Observable,不过翻译过来还是序列顺口些。

Observable 的生命周期

  • observable 发出包含元素的 next 事件。 它可以继续这样做,直到它:
  • …发出 error 事件并终止,或
  • …发出 completed 事件并终止。
  • 一旦 observable 被终止,它不能再发出事件。

将这种概念化的最佳方法之一是使用弹珠图(Marble Diagrams 基于时间轴上绘制的值)。

141526623515_.pic.jpg

151526623523_.pic.jpg

创建 Observable

类型擦除的 ObservableType,也就是 Swift 中的泛型。

它代表了一种推式风格队列。

let observable: Observable = Observable.just(one)

订阅 Observable

订阅 observable sequence 的事件处理程序。

func subscribe(_ on: @escaping (RxSwift.Event<Self.E>) -> Swift.Void) -> Disposable

let one = 1
let two = 2
let three = 3

let observable = Observable.of(one, two, three)

observable.subscribe(onNext: { element in
print(element)
})

在指定的范围内生成一个整数的 observable sequence,使用指定的 scheduler 生成和发送观察者消息。

static func range(start: Self.E, count: Self.E, scheduler: ImmediateSchedulerType = default) -> RxSwift.Observable<Self.E>
let observable = Observable<Int>.range(start: 1, count: 10)
observable
.subscribe(onNext: { i in
let n = Double(i)
let fibonacci = Int(((pow(1.61803, n) - pow(0.61803, n)) /
2.23606).rounded())
print(fibonacci)
})

Disposing and terminating

请记住, 在收到订阅之前, Observable 的内容不会执行任何事情。它是触发一个 Observable 的开始发出事件的订阅, 直到它发出. error 或.completed 完成事件并终止。您可以通过取消对它的订阅来手动终止 Observable。

subscription.dispose()

Managing each subscription individually would be tedious, so RxSwift includes a DisposeBag type. A dispose bag holds disposables — typically added using the .disposed(by:) method — and will call dispose() on each one when the dispose bag is about to be deallocated.

单独管理每个订阅将是单调乏味的, 因此 RxSwift 引入 DisposeBag 类型。DisposeBag 持有 disposable 协议的对象,通常是使用.disposed(by:) 方法, 并将调用 dispose(), 当 DisposeBag 即将 deallocated。

let observable = Observable.of("A", "B", "C")

let subscription = observable.subscribe { event in
print(event)
}
subscription.dispose()

create 操作符接受一个名为 subscribe 的参数。 它的工作是提供对可观察对象进行调用订阅的实现。 换句话说,它定义了将发送给订阅者的所有事件。

static func create(_ subscribe: @escaping (AnyObserver<String>) -> Disposable) -> Observable<String>

Creating observable factories

可以创建一个可观察的工厂,向每个订阅者发布一个新的 Observable,而不是创建一个等待订阅者的 Observable。

static func deferred(_ observableFactory: @escaping () throws -> Observable<Int>) -> Observable<Int>
let disposeBag = DisposeBag()

var flip = false

let factory: Observable<Int> = Observable.deferred {
flip = !flip

if flip {
return Observable.of(1, 2, 3)
} else {
return Observable.of(4, 5, 6)
}
}

for _ in 0...3 {
factory.subscribe(onNext: {
print($0, terminator: "")
})
.disposed(by: disposeBag)

print()
}

Using Traits

Traits是具有比常规 Observable 更窄的行为集合的一种 Observable。它们的使用是可选的;您可以使用任何可能使用性状的规则观察。他们的目的是提供一种给你的 API 或者代码的读者更清楚地表达意图的方式。使用 Traits 暗示的上下文可以帮助您使代码更直观。

RxSwift 中有三种 Traits: Single, Maybe 和 Completable.

Singles 将发出.success(value)或.error 事件。 .success(value)实际上是.next 和.completed 事件的组合。 这对于一次性进程非常有用,它可以成功并产生一个值或失败,例如下载数据或从磁盘加载数据。

下面的例子是读取 Copyright.txt 的文件内容:

let disposeBag = DisposeBag()

enum FileReadError: Error {
case fileNotFound, unreadable, encodingFailed
}

func loadText(from filename: String) -> Single<String> {
return Single.create { single in
let disposable = Disposables.create()

guard let path = Bundle.main.path(forResource: filename, ofType: "txt") else {
single(.error(FileReadError.fileNotFound))
return disposable
}

guard let data = FileManager.default.contents(atPath: path) else {
single(.error(FileReadError.unreadable))
return disposable
}

guard let contents = String(data: data, encoding: .utf8) else {
single(.error(FileReadError.encodingFailed))
return disposable
}

single(.success(contents))

return disposable
}
}

loadText(from: "Copyright")
.subscribe {
switch $0 {
case .success(let string):
print(string)
case .error(let error):
print(error)
}
}
.disposed(by: disposeBag)

Perform side effects

do操作员允许您插入副作用,处理程序执行过程中并不会以任何方式更改发出的事件的操作。

Print debug info

debug运算符, 它将打印 observable 的每个事件的信息。

第三章:Subjects

Subject 是什么

既可以作为发射者,也可以作为观察者,这就是所谓的 Subjects。

RxSwift 中有四个 subject 类型:

  1. PublishSubject: 开始为空, 只向订阅者发出新元素.
  2. BehaviorSubject: 从初始值开始, 将其重播或将最新的元素给新订阅者.
  3. ReplaySubject: 用缓冲区大小, 并将保持元素的缓冲区大小, 并将其重播到新订阅者.
  4. Variable:包装一个 BehaviorSubject,将其当前值保存为状态,并只将最新 / 初始值重播给新订阅者。

PublishSubject

/// Represents an object that is both an observable sequence as well as an observer.
///
/// Each notification is broadcasted to all subscribed observers.
public final class PublishSubject<Element>

当你只是想让订阅者只接受在订阅的时候以后发生的新的事件,直到他们 unsubscribe,或者 subject 已经 terminated 以.completed 或.error 事件的方式,PublishSubject 就可以派上用场。

BehaviorSubject

/// Represents a value that changes over time.
///
/// Observers can subscribe to the subject to receive the last (or initial) value and all subsequent notifications.
public final class BehaviorSubject<Element>

当您希望使用最新数据预先填充 View 时, BehaviorSubject 非常有用。例如, 可以将用户详情页中的控件绑定到 BehaviorSubject, 以便在应用程序获取新数据时, 可以使用最新值来预先填充显示。

WX20180601-164348@2x

ReplaySubject

/// Represents an object that is both an observable sequence as well as an observer.
///
/// Each notification is broadcasted to all subscribed and future observers, subject to buffer trimming policies.
public class ReplaySubject<Element>

ReplaySubject 将临时缓存, 或缓冲区, 它们发出的最新元素, 由您选择的 specified 大小决定。然后, 他们会将该缓冲区重播到新订阅者。

Variable

/// Variable is a wrapper for `BehaviorSubject`.
///
/// Unlike `BehaviorSubject` it can't terminate with error, and when variable is deallocated
/// it will complete its observable sequence (`asObservable`).
///
/// **This concept will be deprecated from RxSwift but offical migration path hasn't been decided yet.**
/// https://github.com/ReactiveX/RxSwift/issues/1501
///
/// Current recommended replacement for this API is `RxCocoa.BehaviorRelay` because:
/// * `Variable` isn't a standard cross platform concept, hence it's out of place in RxSwift target.
/// * It doesn't have a counterpart for handling events (`PublishRelay`). It models state only.
/// * It doesn't have a consistent naming with *Relay or other Rx concepts.
/// * It has an inconsistent memory management model compared to other parts of RxSwift (completes on `deinit`).
///
/// Once plans are finalized, official availability attribute will be added in one of upcoming versions.

public final class Variable<Element> {

Variable 封装 BehaviorSubject 并将其当前值存储为状态。您可以通过其 value 属性访问当前值,并且,与一般的其他 Subject 和 Observable 不同,您还可以使用 value 属性将新元素设置到变量上。换句话说,你不需要使用 onNext(_:)。

let variable = Variable("Initial value")
let disposeBag = DisposeBag()
variable.value = "New initial value"
variable.asObservable()
.subscribe {
print(label: "1)", event: $0)
}
.disposed(by: disposeBag)

variable.value = "1"
variable.asObservable()
.subscribe {
print(label: "2)", event: $0)
}
.disposed(by: disposeBag)

variable.value = "2"

第四章:Observables 和 Subjects 的实践

本章重点是在一个完整的应用开发中使用 RxSwift,一步一步的学会如何把概念应用到实际项目中。

在 view controller 中使用 variable

override func viewDidLoad() {
super.viewDidLoad()

images.asObservable()
.subscribe(onNext: { [weak self] photos in
guard let preview = self?.imagePreview else { return }
preview.image = UIImage.collage(images: photos,
size: preview.frame.size)
})
.disposed(by: bag)

images.asObservable()
.subscribe(onNext: { [weak self] photos in
self?.updateUI(photos: photos)
})
.disposed(by: bag)
}

使用 variable 在 view controller 之间传值

view controller 之间传值可以通过 delegate,但是用 subject 更好。

Talkingtootherviewcontrollersviadelegate

Talkingtootherviewcontrollersviasubjects

@IBAction func actionAdd() {
let photosViewController = storyboard!.instantiateViewController(
withIdentifier: "PhotosViewController") as! PhotosViewController

photosViewController.selectedPhotos
.subscribe(onNext: { [weak self] newImage in
guard let images = self?.images else { return }
images.value.append(newImage)
}, onDisposed: {
print("completed photo selection")
})
.disposed(by: bag)

navigationController!.pushViewController(photosViewController, animated: true)
}

封装已有 API 为 Observable

import Foundation
import UIKit
import Photos

import RxSwift

class PhotoWriter {
enum Errors: Error {
case couldNotSavePhoto
}

static func save(_ image: UIImage) -> Observable<String> {
return Observable.create({ observer in
var savedAssetId: String?
PHPhotoLibrary.shared().performChanges({
let request = PHAssetChangeRequest.creationRequestForAsset(from: image)
savedAssetId = request.placeholderForCreatedAsset?.localIdentifier
}, completionHandler: { success, error in
DispatchQueue.main.async {
if success, let id = savedAssetId {
observer.onNext(id)
observer.onCompleted()
} else {
observer.onError(error ?? Errors.couldNotSavePhoto)
}
}
})
return Disposables.create()
})
}
}

RxSwift traits 实践

  1. Single 只有.success.error 两种事件 。适合作为封装网络接口的返回值,要么成功,要么失败。single
PhotoWriter.save(image)
.asSingle()
.subscribe(onSuccess: { [weak self] id in
self?.showMessage("Saved with id: \(id)")
self?.actionClear()
}, onError: { [weak self] error in
self?.showMessage("Error", description: error.localizedDescription)
})
.disposed(by: bag)

2 . Maybe 有三种事件:.success.completed.error

和 Single 一样,你既可以通过 Maybe.create({...}) 直接创建,也可以使用 .asMaybe()

maybe

  1. Completable 只有.completed.error 两种事件。适合只关心是否完成,而不需要传递 value 的情况。使用方法:Completable.create({...})

completable

订阅自定义的 Observable

例子同上面的 Single