内容简介:RxSwift 是一个组合异步和事件驱动编程的库,通过使用可观察序列和功能样式运算符来,从而允许通过调度程序进行参数化执行。RxSwift 在本质上简化了开发异步程序,允许代码对新数据作出反应,并以顺序和孤立的方式处理它。
RxSwift 是一个组合异步和事件驱动编程的库,通过使用可观察序列和功能样式运算符来,从而允许通过调度程序进行参数化执行。
RxSwift 在本质上简化了开发异步程序,允许代码对新数据作出反应,并以顺序和孤立的方式处理它。
介绍异步编程
使用 Cocoa 和 UIKit 异步的 API 的问题在于:复杂的异步代码变得非常难写,部分原因是苹果 SDK 提供的 API 种类繁多。
异步编程词汇表:
- 状态(State),具体地说,共享可变状态
- 命令式程序设计
- 副作用
- 宣告式編程:允许你定义行为片断, RxSwift 会在有相关事件的时候运行这些行为,并为它们提供一个不可变的,孤立的数据输入。
- 响应式系统(Reactive systems)
反应式系统是一个相当抽象的术语,它涵盖了 Web 或 iOS 应用程序,它们显示了大多数或全部以下特性:
- 反应(Responsive):始终保持 UI 更新,代表了最新的应用程序状态。
- 能復原的(Resilient):每个行为被隔离开来,并提供可恢复的错误恢复。
- 灵活的(Elastic): 该代码处理不同的工作负载,通常实现诸如懒惰驱动数据收集、事件节流和资源共享等特性。
- 消息驱动(Message driven):组件使用基于消息的通信来提高可重用性和隔离性,解耦生命周期和类的实现。
RxSwift 基础
这个标志是一只电鳗。(Rx 项目曾经被称为 Volta)
RxSwift 是微软开源的 ReactiveX 的 Swift 语言的实现。
RxSwift在传统的 Cocoa 编程和纯函数编程之间找到了最佳位置。它允许您对事件作出反应, 方法是使用不可变的代码定义以确定性的、可组合的方式处理异步输入部分。
Rx 代码的三个组成部分是 observables, operators 和 schedulers
Observable
ObservableType 协议 (Observable
- next 下一个事件: “携带” 最新 (或 “下一个 “) 数据值的事件。这是观察者 “接收” 值的方式。
- completed 已完成的事件: 此事件以成功终止事件序列。这意味着可观察的完成其生命周期成功, 不会发出任何其他事件。
- error 错误事件: 可观察的终止带有错误, 不会发出其他事件.
两种不同的可观测序列: finite 和 infinite。
由于它们是高度解耦和可组合的, 所以这些方法通常称为运算符。比如 filter。
运算符也是高度可组合的,它们总是把数据作为输入并输出它们的结果,所以你可以用许多不同的方式轻松地将它们链接起来,实现比单个操作员自己能做的更多的事情。
调度程序是 dispatch queues 的 Rx 等价物。
RxSwift 将充当你的订阅(在左手边下面)和调度器(在右手边)之间的调度器,将工件发送到正确的上下文,并无缝地允许它们与彼此的输出一起工作。
要读取此关系图, 请在不同的计划程序中按预定的顺序 (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
本书中的所有其他示例都使用 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 类,这样您就可以订阅通常有用的事件序列。
安装
官方 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 基于时间轴上绘制的值)。
创建Observable
类型擦除的 ObservableType,也就是 Swift 中的泛型。
它代表了一种推式风格队列。
let observable: Observable
订阅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 类型:
- PublishSubject: 开始为空, 只向订阅者发出新元素.
- BehaviorSubject: 从初始值开始, 将其重播或将最新的元素给新订阅者.
- ReplaySubject: 用缓冲区大小, 并将保持元素的缓冲区大小, 并将其重播到新订阅者.
- 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, 以便在应用程序获取新数据时, 可以使用最新值来预先填充显示。
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 更好。
@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 实践
- Single 只有
.success
或.error
两种事件 。适合作为封装网络接口的返回值,要么成功,要么失败。
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()
- Completable 只有
.completed
和.error
两种事件。适合只关心是否完成,而不需要传递 value 的情况。使用方法:Completable.create({...})
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。