Rxswift旧笔记

455 阅读14分钟

可监听序列

创建序列

使用Observable<>.create创建可监听序列

///typealias,别名
typealias JSON = Any
//创建一个可以被监听的json对象
let json: Observable<JSON> = Observable.create { (observer) -> Disposable in
    //生成一个请求
    let task = URLSession.shared.dataTask(with: ...) { data, _, error in
        //如果error不为空,请求失败,发送error事件
        guard error == nil else {
            observer.onError(error!)
            return
        }
        //json解析失败时,同样发送error事件
        guard let data = data,
            let jsonObject = try? JSONSerialization.jsonObject(with: data, options: .mutableLeaves)
            else {
            observer.onError(DataError.cantParseJSON)
            return
        }
        //拿到请求数据后发送next事件,表示继续往下走,如果还有其它的onNext事件,可以继续触发
        observer.onNext(jsonObject)
        //所有onNext发送完成之后,触发onCompleted事件,表示任务结束
        observer.onCompleted()
    }

    task.resume()
    //如果数据绑定被清除,则取消请求
    return Disposables.create { task.cancel() }
}

如何使用刚刚创建的json:

//subscribe订阅这个对象
json.subscribe(
    //onNext事件观察者
    onNext: { json in
        print("取得 json 成功: \(json)")
    }, onError: { error in
        print("取得 json 失败 Error: \(error.localizedDescription)")
    }, onCompleted: {
        print("取得 json 任务成功完成")
    })
    //disposeBag与某个对象的生命周期绑定,当对象销毁时,销毁disposeBag,同时清除数据绑定
    .disposed(by: disposeBag)

Observable的特性:

  • 多元素:可以有多个onNext
  • 共享附加作用:

使用Single<>.create创建可监听序列

//创建一个方法,返回一个Single对象
func getRepo(_ repo: String) -> Single<[String: Any]> {
    //创建一个Single对象并返回
    return Single<[String: Any]>.create { single in
        let url = URL(string: "https://api.github.com/repos/\(repo)")!
        let task = URLSession.shared.dataTask(with: url) {
            data, _, error in

            if let error = error {
                single(.error(error))
                return
            }

            guard let data = data,
                  let json = try? JSONSerialization.jsonObject(with: data, options: .mutableLeaves),
                  let result = json as? [String: Any] else {
                single(.error(DataError.cantParseJSON))
                return
            }
            //single只能发送单一的success事件,无法像Observable一样发送多个onNext事件
            single(.success(result))
            //因为只能发送一个success事件,所以无需onCompleted来表示已完成
        }

        task.resume()

        return Disposables.create { task.cancel() }
    }
}

如何使用刚刚创建的getRepo方法:

getRepo("ReactiveX/RxSwift")
    .subscribe(onSuccess: { json in
        print("JSON: ", json)
    }, onError: { error in
        print("Error: ", error)
    })
    .disposed(by: disposeBag)

Single的特性:

  • 单元素:只有一个onSuccess
  • 不共享附加作用:每次subscribe订阅,都会触发请求
  • 可以对 Observable 调用.asSingle()方法,将它转换为Single

使用Completable.create创建可监听序列

func cacheLocally() -> Completable {
    return Completable.create { completable in
       // Store some data locally
       ...
       ...

       guard success else {
           completable(.error(CacheError.failedCaching))
           return Disposables.create {}
       }

       completable(.completed)
       return Disposables.create {}
    }
}

如何使用刚刚创建的cacheLocally方法:

cacheLocally()
    .subscribe(onCompleted: {
        print("Completed with no error")
    }, onError: { error in
        print("Completed with an error: \(error.localizedDescription)")
     })
    .disposed(by: disposeBag)
Bag)

Completable的特性:

  • 单元素:只有一个onCompleted
  • 不共享附加作用:每次subscribe订阅,都会触发请求
  • onCompleted不带参数

使用Maybe<>.create创建可监听序列

func generateString() -> Maybe<String> {
    return Maybe<String>.create { maybe in
        //要么返回一个success事件,可以带参数
        maybe(.success("RxSwift"))

        // OR
        //要么返回一个completed事件
        maybe(.completed)

        // OR
        //要么返回一个error事件
        maybe(.error(error))

        return Disposables.create {}
    }
}

使用:

generateString()
    .subscribe(onSuccess: { element in
        print("Completed with element \(element)")
    }, onError: { error in
        print("Completed with an error \(error.localizedDescription)")
    }, onCompleted: {
        print("Completed with no element")
    })
    .disposed(by: disposeBag)

Maybe的特性:

  • 单元素:只会触发三个元素中的一个
  • 不共享附加作用:每次subscribe订阅,都会触发请求
  • onCompleted不带参数

使用Driver简化UI层的代码

使用Driver的序列具有以下特征

  • 不会产生 error 事件
  • 一定在 MainScheduler 监听(主线程监听)
  • 共享附加作用

为什么要使用Driver?

Driver表示这个序列不会产生错误事件,并且一定在主线程监听,非常适合用来绑定UI元素.
因为如果在其它线程刷新UI,会导致崩溃.
:在searchBar搜索框里输入文本,然后发请求,把请求结果展示在tableView里,并把结果个数显示在Label里. 代码如下:

let results = searchBar.rx.text
    .throttle(0.3, scheduler: MainScheduler.instance)
    .flatMapLatest { searchBar in
        //发请求
        fetchAutoCompleteItems(searchBar) 
    }

results
    .map { "\($0.count)" }
    .bind(to: resultCountLabel.rx.text)
    .disposed(by: disposeBag)

results
    .bind(to: resultsTableView.rx.items(cellIdentifier: "Cell")) {
      (_, result, cell) in
        cell.textLabel?.text = "\(result)"
    }
    .disposed(by: disposeBag)

这段代码存在着如下问题:

  1. fetchAutoCompleteItems()请求失败时,会导致序列中断,并不会刷新UI;
  2. 请求结果返回时,如果不在主线程,那么刷新UI会导致崩溃;
  3. 返回结果bindTo两个UI元素上,如果没有共享附加作用,比如fetchAutoCompleteItems()是用Single创建的,那么就会发两次请求;
    以上3点不符合driver序列,改造后代码如下:
let results = query.rx.text
    .throttle(0.3, scheduler: MainScheduler.instance)
    .flatMapLatest { query in
        fetchAutoCompleteItems(query)
            .observeOn(MainScheduler.instance)  // 结果在主线程返回
            //如果请求失败,那么处理错误保证序列继续往下进行
            //可以理解为,把本来该走.onError()事件,改为走.onSuccess([])
            .catchErrorJustReturn([])          
    }
    //share表示共享附加作用,这里指共享网络请求
    .share(replay: 1)                             // HTTP 请求是被共享的

results
    .map { "\($0.count)" }
    .bind(to: resultCount.rx.text)
    .disposed(by: disposeBag)

results
    .bind(to: resultsTableView.rx.items(cellIdentifier: "Cell")) {
      (_, result, cell) in
        cell.textLabel?.text = "\(result)"
    }
    .disposed(by: disposeBag)

经过复杂的处理后,解决了上述问题,但是写法太过复杂,容易出错.直接改用Driver写法,如下:

/**
asDriver()将普通序列转换成Driver序列,该序列会强制满足Driver3要素,避免了手动处理;

*/
let results = query.rx.text.asDriver()        // 将普通序列转换为 Driver
    .throttle(0.3, scheduler: MainScheduler.instance)
    .flatMapLatest { query in
        fetchAutoCompleteItems(query)
            //如果序列本身包含了Error事件,需要使用onErrorJustReturn: 提供抛错时的默认返回值
            .asDriver(onErrorJustReturn: []) 
    }

results
    .map { "\($0.count)" }
    .drive(resultCount.rx.text)               // 这里改用 `drive` 而不是 `bindTo`
    .disposed(by: disposeBag)                 // 这样可以确保必备条件都已经满足了

results
    .drive(resultsTableView.rx.items(cellIdentifier: "Cell")) {
      (_, result, cell) in
        cell.textLabel?.text = "\(result)"
    }
    .disposed(by: disposeBag)

使用Signal处理事件序列

Signal 和 Driver 相似,唯一的区别是,Driver 会对新观察者回放(重新发送)上一个元素,而 Signal 不会对新观察者回放上一个元素。

let button: UIButton = ...
let showAlert: (String) -> Void = ...

let event: Driver<Void> = button.rx.tap.asDriver()

let observer: () -> Void = { showAlert("弹出提示框1") }
event.drive(onNext: observer)

// ... 假设以下代码是在用户点击 button 后运行

let newObserver: () -> Void = { showAlert("弹出提示框2") }
//使用Driver时,会接受Button上一次的点击事件,从而触发newObserver
event.drive(onNext: newObserver)

使用Driver时,会接受Button上一次的点击事件,从而触发newObserver.这里应该用Signal:

...

let event: Signal<Void> = button.rx.tap.asSignal()

let observer: () -> Void = { showAlert("弹出提示框1") }
event.emit(onNext: observer)

// ... 假设以下代码是在用户点击 button 后运行

let newObserver: () -> Void = { showAlert("弹出提示框2") }
event.emit(onNext: newObserver)

Signal不管订阅的对象在之前已经发送了多少事件,只管在订阅后发送的事件
Driver使用.asDriver()转换,使用.drive()订阅;Signal使用.asSignal()转换,使用.emit()订阅

使用ControlProperty描述UI 的属性(既是可监听序列又是观察者)

ControlProperty特征

  • 不会产生 error 事件
  • 一定在 MainScheduler 订阅(主线程订阅)
  • 一定在 MainScheduler 监听(主线程监听)
  • 共享状态变化

可以用来把自定义UI控件的某个属性,描述为可被监听的属性:
比如RxSwift把UITextField的text设置为可被监听的属性

import RxSwift
import UIKit
 
extension Reactive where Base: UITextField {
 
    public var text: ControlProperty<String?> {
        return value
    }
 
    public var value: ControlProperty<String?> {
        return base.rx.controlPropertyWithDefaultEvents(
            getter: { textField in
                textField.text
        },
            setter: { textField, value in
                if textField.text != value {
                    textField.text = value
                }
        }
        )
    }
     
    //......
}

使用ControlEvent描述UI 所产生的事件

ControlEvent特征和ControlProperty一致
可以用来把自定义UI控件的某个方法,描述为可被监听的方法:
比如UIButton 的 rx.tap 方法类型便是 ControlEvent:

import RxSwift
import UIKit
 
extension Reactive where Base: UIButton {
    public var tap: ControlEvent<Void> {
        return controlEvent(.touchUpInside)
    }
}

ControlEvent来重写UIViewController的生命周期,方便监听:

import UIKit
import RxCocoa
import RxSwift
 
public extension Reactive where Base: UIViewController {
    public var viewDidLoad: ControlEvent<Void> {
        //调用父类的viewDidLoad方法
        let source = self.methodInvoked(#selector(Base.viewDidLoad)).map { _ in }
        return ControlEvent(events: source)
    }
}

使用时:

//页面加载完毕
self.rx.viewDidLoad
    .subscribe(onNext: {
        print("viewDidLoad")
    }).disposed(by: disposeBag)

如何选择可监听序列的创建方式

Observable

适用于需要监听多个元素的情况,如:
空调温度,20度时开制热,30度时开制冷,等

Single

适用于要么成功要么失败,并且成功时能拿到数据的情况,比如网络请求

Completable

同样适用于要么成功要么失败,但是并不关心成功时的返回值的情况,比如文件处理.

Maybe

适用于要么成功要么失败,可能需要参数,也可能不需要参数的情况.

Driver

适用于UI元素状态的处理,比如更新文本.

Signal

适用于UI元素事件的处理,比如按钮点击.

ControlProperty

适用与把自定义UI的属性注册为被观察者

ControlEvent

适用与把自定义UI的方法注册为被观察者


观察者

订阅某个序列后,触发信号时需要执行的动作

AnyObserver

通常的写法是:

URLSession.shared.rx.data(request: URLRequest(url: url))
    .subscribe(onNext: { data in
        print("Data Task Success with count: \(data.count)")
    }, onError: { error in
        print("Data Task Error: \(error)")
    })
    .disposed(by: disposeBag)

.subscribe()里面的,就是观察者:

let observer: AnyObserver<Data> = AnyObserver { (event) in
    switch event {
    case .next(let data):
        print("Data Task Success with count: \(data.count)")
    case .error(let error):
        print("Data Task Error: \(error)")
    default:
        break
    }
}

URLSession.shared.rx.data(request: URLRequest(url: url))
    .subscribe(observer)
    .disposed(by: disposeBag)

Binder

Binder因为具有以下特征:

  • 不会处理错误事件
  • 确保绑定都是在给定 Scheduler 上执行(默认 MainScheduler)

常用来创建创建UI观察者

  • 按钮是否可点击 button.rx.isEnabled
extension Reactive where Base: UIControl {
  public var isEnabled: Binder<Bool> {
      return Binder(self.base) { control, value in
          control.isEnabled = value
      }
  }
}
  • label 的当前文本 label.rx.text
extension Reactive where Base: UILabel {
  public var text: Binder<String?> {
      return Binder(self.base) { label, text in
          label.text = text
      }
  }
}

Observable & Observer 既是可监听序列也是观察者

辅助类型既是观察者也是可监听序列,能对Observable做相应的约束,满足自己应用的要求

AsyncSubject(最后一个元素)

AsyncSubject 将在源 Observable 产生完成事件后,发出最后一个元素(仅仅只有最后一个元素)
演示:

let disposeBag = DisposeBag()
let subject = AsyncSubject<String>()

subject
  .subscribe { print("Subscription: 1 Event:", $0) }
  .disposed(by: disposeBag)

subject.onNext("🐶")
subject.onNext("🐱")
subject.onNext("🐹")
subject.onCompleted()

输出结果:

Subscription: 1 Event: next(🐹)
Subscription: 1 Event: completed

PublishSubject(订阅后产生的元素)

PublishSubject 将对观察者发送订阅后产生的元素,而在订阅前发出的元素将不会发送给观察者
演示:

let disposeBag = DisposeBag()
let subject = PublishSubject<String>()

subject
  .subscribe { print("Subscription: 1 Event:", $0) }
  .disposed(by: disposeBag)

subject.onNext("🐶")
subject.onNext("🐱")

subject
  .subscribe { print("Subscription: 2 Event:", $0) }
  .disposed(by: disposeBag)

subject.onNext("🅰️")
subject.onNext("🅱️")

输出结果:

Subscription: 1 Event: next(🐶)
Subscription: 1 Event: next(🐱)
Subscription: 1 Event: next(🅰️)
Subscription: 2 Event: next(🅰️)
Subscription: 1 Event: next(🅱️)
Subscription: 2 Event: next(🅱️)

ReplaySubject(全部或者订阅前指定要求的元素)

ReplaySubject 将对观察者发送全部的元素,无论观察者是何时进行订阅的。

这里存在多个版本的 ReplaySubject,有的只会将最新的 n 个元素发送给观察者,有的只会将限制时间段内最新的元素发送给观察者。
演示:

let disposeBag = DisposeBag()
let subject = ReplaySubject<String>.create(bufferSize: 1)

subject
  .subscribe { print("Subscription: 1 Event:", $0) }
  .disposed(by: disposeBag)

subject.onNext("🐶")
subject.onNext("🐱")

subject
  .subscribe { print("Subscription: 2 Event:", $0) }
  .disposed(by: disposeBag)

subject.onNext("🅰️")
subject.onNext("🅱️")

输出结果:

Subscription: 1 Event: next(🐶)
Subscription: 1 Event: next(🐱)
Subscription: 2 Event: next(🐱)
Subscription: 1 Event: next(🅰️)
Subscription: 2 Event: next(🅰️)
Subscription: 1 Event: next(🅱️)
Subscription: 2 Event: next(🅱️)

BehaviorSubject(最新的元素,没有就默认)

演示:

let disposeBag = DisposeBag()
let subject = BehaviorSubject(value: "🔴")

subject
  .subscribe { print("Subscription: 1 Event:", $0) }
  .disposed(by: disposeBag)

subject.onNext("🐶")
subject.onNext("🐱")

subject
  .subscribe { print("Subscription: 2 Event:", $0) }
  .disposed(by: disposeBag)

subject.onNext("🅰️")
subject.onNext("🅱️")

输出结果:

Subscription: 1 Event: next(🔴)
Subscription: 1 Event: next(🐶)
Subscription: 1 Event: next(🐱)
Subscription: 2 Event: next(🐱)
Subscription: 1 Event: next(🅰️)
Subscription: 2 Event: next(🅰️)
Subscription: 1 Event: next(🅱️)
Subscription: 2 Event: next(🅱️)
Subscription: 3 Event: next(🅱️)

ControlProperty(描述 UI 控件属性)

辅助类型使用时,如果源Observable因为产生了error事件而终止,那么辅助类型就不会发出任何元素而是发出error事件

Variable(弃用)

Operator - 操作符

创建

create 自定义逻辑

create 操作符将创建一个 Observable,你需要提供一个构建函数,在构建函数里面描述事件(next,error,completed)的产生过程。

deferred 每次订阅时创建

直到订阅发生,才创建 Observable,并且为每位订阅者创建全新的 Observable

empty 只有一个完成事件

创建一个只有一个完成事件的Observable。 演示:

let id = Observable<Int>.empty()
//相当于
let id = Observable<Int>.create { observer in
    observer.onCompleted()
    return Disposables.create()
}

error 只有error事件

创建一个只有 error 事件的 Observable. 演示:

let error: Error = ...
let id = Observable<Int>.error(error)
//相当于
let id = Observable<Int>.create { observer in
    observer.onError(error)
    return Disposables.create()
}

never 永远不会发出元素

创建一个永远不会发出元素的 Observable 演示:

let id = Observable<Int>.never()
//相当于
let id = Observable<Int>.create { observer in
    return Disposables.create()
}

repeatElement 重复的产生某一个元素

repeatElement 操作符将创建一个 Observable,这个 Observable 将无止尽的发出同一个元素。
演示:

let id = Observable.repeatElement(0)
//相当于
let id = Observable<Int>.create { observer in
    observer.onNext(0)
    observer.onNext(0)
    observer.onNext(0)
    observer.onNext(0)
    ... // 无数次
    return Disposables.create()
}

just 产生特定的一个元素

创建 Observable 发出唯一的一个元素 演示:

let id = Observable.just(0)
//相当于
let id = Observable<Int>.create { observer in
    observer.onNext(0)
    observer.onCompleted()
    return Disposables.create()
}

from 从一个序列拉取元素

将其他类型或者数据结构转换为 Observable; 演示:

//将数组转换成Observable
let numbers = Observable.from([0, 1, 2])
//相当于
let numbers = Observable<Int>.create { observer in
    observer.onNext(0)
    observer.onNext(1)
    observer.onNext(2)
    observer.onCompleted()
    return Disposables.create()
}
let optional: Int? = 1
//将可选值转换成Observable
let value = Observable.from(optional: optional)
//相当于
let value = Observable<Int>.create { observer in
    if let element = optional {
        observer.onNext(element)
    }
    observer.onCompleted()
    return Disposables.create()
}

interval 定时

创建一个 Observable 每隔一段时间,发出一个索引数; interval 操作符将创建一个 Observable,它每隔一段设定的时间,发出一个索引数的元素。它将发出无数个元素。 演示:

//创建一个Observable:主线程的秒定时器,每秒发送一个next
let intSequence = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
    //publish待连接,连接前不会发送next,
    .publish()
//订阅,因为没有connect,即使订阅也不会发送next
_ = intSequence
    .subscribe(onNext: { print("Subscription 1:, Event: \($0)") })
//2秒之后,开始连接,定时器开始发next事件
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
    _ = intSequence.connect()
}
//4秒之后,增加一个订阅
DispatchQueue.main.asyncAfter(deadline: .now() + 4) {
  _ = intSequence
      .subscribe(onNext: { print("Subscription 2:, Event: \($0)") })
}
//6秒之后,增加一个订阅
DispatchQueue.main.asyncAfter(deadline: .now() + 6) {
  _ = intSequence
      .subscribe(onNext: { print("Subscription 3:, Event: \($0)") })
}

timer 延时

  • 创建一个 Observable 在一段延时后,产生唯一的一个元素
  • 创建一个 Observable 在一段延时后,每隔一段时间产生一个元素

组合Observables

merge 任意一个 Observable 产生了元素,就发出这个元素

将多个 Observables 合并成一个;
通过使用 merge 操作符你可以将多个 Observables 合并成一个,当某一个 Observable 发出一个元素时,他就将这个元素发出。

注意⚠️:如果,某一个 Observable 发出一个 onError 事件,那么被合并的 Observable 也会将它发出,并且立即终止序列。

演示:

let disposeBag = DisposeBag()

let subject1 = PublishSubject<String>()
let subject2 = PublishSubject<String>()
Observable.of(subject1, subject2)
    .merge()
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
subject1.onNext("🅰️")
subject1.onNext("🅱️")
subject2.onNext("①")
subject2.onNext("②")
subject1.onNext("🆎")
subject2.onNext("③")

输出:

🅰️
🅱️
①
②
🆎
③

concat 当前Observable发完后下一个才发出

concat 操作符将多个 Observables 按顺序串联起来,当前一个 Observable 元素发送完毕后,后一个 Observable 才可以开始发出元素。

concat 将等待前一个 Observable 产生完成事件后,才对后一个 Observable 进行订阅。如果后一个是“热” Observable ,在它前一个 Observable 产生完成事件前,所产生的元素将不会被发送出来。 演示:

let disposeBag = DisposeBag()
//发送上一个 event 以及之后新创建的 event
let subject1 = BehaviorSubject(value: "🍎")
let subject2 = BehaviorSubject(value: "🐶")

//发送上一个 event 以及之后新创建的 event,subject1和subject2在这里作为可监听序列被监听
let variable = Variable(subject1)

//订阅subject1时,发送上一个event🍎
variable.asObservable()
        //如果不加concat,print出来就是subject1自身,而不是🍎
        .concat()
        .subscribe { print($0) }
        .disposed(by: disposeBag)
//发送最新的event🍐
subject1.onNext("🍐")
//发送最新的event🍊
subject1.onNext("🍊")
//修改value时,等于订阅subject2,发出默认event🐶
variable.value = subject2

//subject2发出event,但是由于concat特性,subject1还没onCompleted,所以subject2并不会触发观察者print
subject2.onNext("I would be ignored")
//BehaviorSubject特性:subject2发出的I would be ignored事件没有被监听,就被🐱覆盖
subject2.onNext("🐱")
//subject1结束后,concat特性:会自动走subject2序列,这个时候,会发送subject2上一个 event🐱 以及之后新创建的 event🐭
subject1.onCompleted()
subject2.onNext("🐭")
//variable销毁时,会自动发送onCompleted

输出结果:

next(🍎)
next(🍐)
next(🍊)
next(🐱)
next(🐭)

组合多个 Observables 的元素

zip 当每一个 Observable 都发出一个新的元素

通过一个函数将多个 Observables 的元素组合起来,然后将每一个组合的结果发出来

  • 最多不超过8个Observables
  • 严格的按照序列的索引数进行组合。例如,返回的 Observable 的第一个元素,是由每一个源 Observables 的第一个元素组合出来的。它的第二个元素 ,是由每一个源 Observables 的第二个元素组合出来的。它的第三个元素 ,是由每一个源 Observables 的第三个元素组合出来的,以此类推。
  • 它的元素数量等于源 Observables 中元素数量最少的那个。

演示:

let disposeBag = DisposeBag()
let first = PublishSubject<String>()
let second = PublishSubject<String>()

Observable.zip(first, second) { $0 + $1 }
          .subscribe(onNext: { print($0) })
          .disposed(by: disposeBag)

first.onNext("1")
second.onNext("A")
first.onNext("2")
second.onNext("B")
second.onNext("C")
second.onNext("D")
first.onNext("3")
first.onNext("4")

输出:

1A
2B
3C
4D

combineLatest 任意一个 Observable 发出一个新的元素

combineLatest 操作符将多个 Observables 中最新的元素通过一个函数组合起来,然后将这个组合的结果发出来。这些源 Observables 中任何一个发出一个元素,他都会发出一个元素(前提是,这些 Observables 曾经发出过元素)。
演示:

let disposeBag = DisposeBag()

let first = PublishSubject<String>()
let second = PublishSubject<String>()

Observable.combineLatest(first, second) { $0 + $1 }
          .subscribe(onNext: { print($0) })
          .disposed(by: disposeBag)

first.onNext("1")
second.onNext("A")
first.onNext("2")
second.onNext("B")
second.onNext("C")
second.onNext("D")
first.onNext("3")
first.onNext("4")

输出结果:

1A
2A
2B
2C
2D
3D
4D

转换

map 对每个元素直接转换

将源 Observable 的每一个元素应用一个转换方法,将他们转换成 另一个元素。 演示:

struct Person {
    var name:String!
    var age:Int!
    init(dic:Dictionary<String,Any>) {
        self.name = dic["name"] as? String
        self.age = dic["age"] as? Int
    }
}

let disposeBag = DisposeBag()

let arr:[Dictionary<String,Any>] = [
            ["name":"张三","age":23],
            ["name":"李四","age":33],
            ["name":"王五","age":22],]
        
Observable.from(arr)
    .map { Person.init(dic: $0) }
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

输出:

Person(name: Optional("张三"), age: Optional(23))
Person(name: Optional("李四"), age: Optional(33))
Person(name: Optional("王五"), age: Optional(22))

这里将Dictionary元素转换成了Person结构体

flatMap 转换到另一个 Observable

flatMap 操作符将源 Observable 的每一个元素应用一个转换方法,将他们转换成 Observables。 然后将这些 Observables 的元素合并之后再发送出来。
例如,当 Observable 的元素本身拥有其他的 Observable 时,你可以将所有子 Observables 的元素发送出来。 演示:

let A = BehaviorSubject(value: "A的默认消息")
let B = BehaviorSubject(value: "B的默认消息")

Observable.of(A,B)//同时观察了A,B
.flatMap {$0} //将Observable的元素转换成其他的Observable",把对 A/B 的观察" 转成 "A/B 观察的字符串"
.subscribe(onNext: {print($0)})
.disposed(by: disposeBag)

A.onNext("A-2")
A.onNext("A-3")
B.onNext("B-2")
B.onNext("B-3")
B.onNext("B-4")

输出:

A的默认消息
B的默认消息
A-2
A-3
B-2
B-3
B-4

flatMapLatest 只接收最新的元素转换的 Observable 所产生的元素

将 Observable 的元素转换成其他的Observable,然后取这些 Observables中最新的一个。 flatMapLatest操作符将源Observable 的每一个元素应用一个转换方法,将他们转换成Observables。一旦转换出一个新的 Observable,就只发出它的元素,旧的 Observables的元素将被忽略掉。 演示:

let A = BehaviorSubject(value: "A的默认消息")
let B = BehaviorSubject(value: "B的默认消息")

Observable.of(A,B)
.flatMapLatest {$0}
.subscribe(onNext: {print($0)})
.disposed(by: disposeBag)

A.onNext("A-2")
A.onNext("A-3")
B.onNext("B-2")
B.onNext("B-3")
B.onNext("B-4")

输出结果:

A的默认消息
B的默认消息
B-2
B-3
B-4

concatMap 每一个元素转换的 Observable 按顺序产生元素

concatMap 操作符将源 Observable 的每一个元素应用一个转换方法,将他们转换成 Observables。然后让这些 Observables 按顺序的发出元素,当前一个 Observable 元素发送完毕后,后一个 Observable 才可以开始发出元素。等待前一个 Observable 产生完成事件后,才对后一个 Observable 进行订阅。 演示:

...
variable.asObservable()
        //将subject1的元素,通过{$0}转换成Observables
        .concatMap{$0}
        .subscribe { print($0) }
        .disposed(by: disposeBag)

输出结果没变:

next(🍎)
next(🍐)
next(🍊)
next(🐱)
next(🐭)

concatMap 和 concat 的区别:
concatMap:将源 Observable 的每一个元素应用一个转换方法,将他们转换成 Observables
concat:操作符将多个 Observables 按顺序串联起来

将产生的事件封装成元素发送出来

materialize

将序列产生的事件,转换成元素 演示:

let disposeBag = DisposeBag()
Observable.of(1,2,3)
    .materialize()
    .subscribe(onNext:{print($0)})
    .disposed(by: disposeBag)

输出:

next(1)
next(2)
next(3)
completed

dematerialize

将 materialize 转换后的元素还原 演示:

let disposeBag = DisposeBag()
Observable.of(1,2,3)
    .materialize()
    .dematerialize()
    .subscribe(onNext:{print($0)})
    .disposed(by: disposeBag)

输出:

1
2
3

延迟

delay 延迟发出元素

将 Observable 的每一个元素拖延一段时间后发出

delaySubscription 延迟订阅

延时订阅.delaySubscription 操作符将在经过所设定的时间后,才对 Observable 进行订阅操作。

delay是延时发元素,delaySubscription是延时订阅

过滤元素

filter 通过判定条件过滤出一些元素

filter 操作符将通过你提供的判定方法过滤一个 Observable。 演示:

let disposeBag = DisposeBag()
Observable.of(2, 30, 22, 5, 60, 1)
          .filter { $0 > 10 }
          .subscribe(onNext: { print($0) })
          .disposed(by: disposeBag)

输出:

30
22
60

take 只取头 n 个元素

只发出头 n 个元素。并且忽略掉后面的元素,直接结束序列。 演示:

let disposeBag = DisposeBag()

Observable.of("🐱", "🐰", "🐶", "🐸", "🐷", "🐵")
    .take(3)
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

输出:

🐱
🐰
🐶

takeLast 只取尾 n 个元素

只发出尾部 n 个元素。并且忽略掉前面的元素。 演示:

let disposeBag = DisposeBag()

Observable.of("🐱", "🐰", "🐶", "🐸", "🐷", "🐵")
    .takeLast(3)
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

输出:

🐸
🐷
🐵

takeUntil 只取第二个Observable发出元素前发出的元素

忽略掉在第二个 Observable 产生事件后发出的那部分元素

takeUntil 操作符将镜像源 Observable,它同时观测第二个 Observable。一旦第二个 Observable 发出一个元素或者产生一个终止事件,那个镜像的 Observable 将立即终止。

演示:

let disposeBag = DisposeBag()

let sourceSequence = PublishSubject<String>()
let referenceSequence = PublishSubject<String>()

sourceSequence
    .takeUntil(referenceSequence)
    .subscribe { print($0) }
    .disposed(by: disposeBag)

sourceSequence.onNext("🐱")
sourceSequence.onNext("🐰")
sourceSequence.onNext("🐶")

referenceSequence.onNext("🔴")

sourceSequence.onNext("🐸")
sourceSequence.onNext("🐷")
sourceSequence.onNext("🐵")

输出:

next(🐱)
next(🐰)
next(🐶)
completed

takeWhile 只取判定为true的元素

镜像一个 Observable 直到某个元素的判定为 false

takeWhile 操作符将镜像源 Observable 直到某个元素的判定为 false。此时,这个镜像的 Observable 将立即终止。

演示:

let disposeBag = DisposeBag()

Observable.of(1, 2, 3, 4, 3, 2, 1)
    .takeWhile { $0 < 4 }
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

输出:

1
2
3

amb 从多个 Observables 中,只取第一个产生元素的 Observable

在多个源 Observables 中, 取第一个发出元素或产生事件的 Observable,然后只发出它的元素

elementAt 只取第n个元素

只发出 Observable 中的第 n 个元素;elementAt 操作符将拉取 Observable 序列中指定索引数的元素,然后将它作为唯一的元素发出。 演示:

let disposeBag = DisposeBag()
Observable.of(1,2,3)
    .elementAt(1)
    .subscribe(onNext:{print($0)})
    .disposed(by: disposeBag)

输出:

2

sample 通过另一个Observable对源抽样取元素

sample 操作符将不定期的对源 Observable 进行取样操作。通过第二个 Observable 来控制取样时机。一旦第二个 Observable 发出一个元素,就从源 Observable 中取出最后产生的元素。

debounce 在指定时间内只接受最新数据

debounce 操作符将发出过滤元素,在指定时间内,只接受最新数据 演示:

let disposeBag = DisposeBag()
let subject = PublishSubject<String>()

subject
    .debounce(.seconds(2), scheduler: MainScheduler.instance)
  .subscribe {
    print("Subscription: 1 Event:", $0)
    	
}
  .disposed(by: disposeBag)

subject.onNext("1")
subject.onNext("2")
subject.onNext("3")
subject.onNext("🐱")

输出结果:

==实际测试时无任何输出,原因不明==

distinctUntilChanged 直到元素的值发生变化才发出新的元素

阻止 Observable 发出相同的元素。如果后一个元素和前一个元素是相同的,那么这个元素将不会被发出来。如果后一个元素和前一个元素不相同,那么这个元素才会被发出来。 演示:

let disposeBag = DisposeBag()

Observable.of("🐱", "🐷", "🐱", "🐱", "🐱", "🐵", "🐱")
    .distinctUntilChanged()
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

输出:

🐱
🐷
🐱
🐵
🐱

skip 跳过头 n 个元素

skip 操作符可以让你跳过 Observable 中头 n 个元素,只关注后面的元素。 演示:

let disposeBag = DisposeBag()

Observable.of("🐱", "🐰", "🐶", "🐸", "🐷", "🐵")
    .skip(2)
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

输出:

🐶
🐸
🐷
🐵

skipUntil 跳过头几个元素直到另一个 Observable 发出一个元素

跳过 Observable 中头几个元素,直到另一个 Observable 发出一个元素。
skipUntil 操作符可以让你忽略源 Observable 中头几个元素,直到另一个 Observable 发出一个元素后,它才镜像源 Observable。 演示:

let disposeBag = DisposeBag()

let sourceSequence = PublishSubject<String>()
let referenceSequence = PublishSubject<String>()

sourceSequence
    .skipUntil(referenceSequence)
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

sourceSequence.onNext("🐱")
sourceSequence.onNext("🐰")
sourceSequence.onNext("🐶")

referenceSequence.onNext("🔴")

sourceSequence.onNext("🐸")
sourceSequence.onNext("🐷")
sourceSequence.onNext("🐵")

输出:

🐸
🐷
🐵

skipWhile 跳过判定为true的元素

skipWhile 操作符可以让你忽略源 Observable 中头几个元素,直到元素的判定为否后,它才镜像源 Observable。 演示:

let disposeBag = DisposeBag()

Observable.of(1, 2, 3, 4, 3, 2, 1)
    .skipWhile { $0 < 4 }
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

输出:

4
3
2
1

ignoreElements 忽略所有next的元素

忽略掉所有的元素,只发出 error 或 completed 事件; ignoreElements 操作符将阻止 Observable 发出 next 事件,但是允许他发出 error 或 completed 事件。

startWith 头部插入元素

startWith 操作符会在 Observable 头部插入一些元素。 演示:

let disposeBag = DisposeBag()

Observable.of("🐶", "🐱", "🐭", "🐹")
    .startWith("1")
    .startWith("2", "🅰️️")
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

输出:

2
🅰️
1
🐶
🐱
🐭
🐹

publish 通知可被连接后才发出元素

publish 会将 Observable 转换为可被连接的 Observable。可被连接的 Observable 和普通的 Observable 十分相似,不过在被订阅后不会发出元素,直到 connect 操作符被应用为止。这样一来你可以控制 Observable 在什么时候开始发出元素。

connect 通知可被连接

connect 通知可被连接的 Observable可以发出元素了
演示:

//创建一个Observable:主线程的秒定时器,每秒发送一个next
let intSequence = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
    //publish待连接,连接前不会发送next,
    .publish()
//订阅,因为没有connect,即使订阅也不会发送next
_ = intSequence
    .subscribe(onNext: { print("Subscription 1:, Event: \($0)") })
//2秒之后,开始连接,定时器开始发next事件
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
    _ = intSequence.connect()
}
//4秒之后,增加一个订阅
DispatchQueue.main.asyncAfter(deadline: .now() + 4) {
  _ = intSequence
      .subscribe(onNext: { print("Subscription 2:, Event: \($0)") })
}
//6秒之后,增加一个订阅
DispatchQueue.main.asyncAfter(deadline: .now() + 6) {
  _ = intSequence
      .subscribe(onNext: { print("Subscription 3:, Event: \($0)") })
}

refCount 与publish相反

将可被连接的 Observable 转换为普通 Observable 演示:

//每隔1秒钟发送1个事件
let interval = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
                                .publish()
                                .refCount()
//第一个订阅者(立刻开始订阅)
_ = interval
    .subscribe(onNext: { print("订阅1: \($0)") })

//第二个订阅者(延迟5秒开始订阅)
delay(5) {
    _ = interval
        .subscribe(onNext: { print("订阅2: \($0)") })
}

缓存/组合元素

buffer 数量或时间

buffer 操作符将缓存 Observable 中发出的新元素,当元素达到某个数量,或者经过了特定的时间,它就会将这个元素集合发送出来。

window

将 Observable 分解为多个子 Observable,周期性的将子 Observable 发出来

window 操作符和 buffer 十分相似,buffer 周期性的将缓存的元素集合发送出来,而 window 周期性的将元素集合以 Observable 的形态发送出来。 buffer 要等到元素搜集完毕后,才会发出元素序列。而 window 可以实时发出元素序列。

groupBy

groupBy 操作符将源 Observable 分解为多个子 Observable,然后将这些子 Observable 发送出来。 也就是说该操作符会将元素通过某个键进行分组,然后将分组后的元素序列以 Observable 的形态发送出来。 演示:

let disposeBag = DisposeBag()

//将奇数偶数分成两组
Observable<Int>.of(0, 1, 2, 3, 4, 5)
    .groupBy(keySelector: { (element) -> String in
        return element % 2 == 0 ? "偶数" : "基数"
    })
    .subscribe(onNext: { (group) in
        group.subscribe(onNext: { (x) in
            print("key:\(group.key)    event:\(x)")
            })
            .disposed(by: disposeBag)
    })
.disposed(by: disposeBag)

输出:

key:偶数    event0
key:基数    event1
key:偶数    event2
key:基数    event3
key:偶数    event4
key:基数    event5

错误处理

catchError 拦截错误事件

catchError 操作符将会拦截一个 error 事件,将它替换成其他的元素或者一组元素,然后传递给观察者。这样可以使得 Observable 正常结束,或者根本都不需要结束。

这里存在其他版本的 catchError 操作符。 演示:

let disposeBag = DisposeBag()

let sequenceThatFails = PublishSubject<String>()
let recoverySequence = PublishSubject<String>()

sequenceThatFails
    .catchError {
        print("Error:", $0)
        //这里的return 把当前序列替换成了recoverySequence
        return recoverySequence
    }
    .subscribe { print($0) }
    .disposed(by: disposeBag)

sequenceThatFails.onNext("😬")
sequenceThatFails.onNext("😨")
sequenceThatFails.onNext("😡")
sequenceThatFails.onNext("🔴")
sequenceThatFails.onError(TestError.test)

recoverySequence.onNext("😊")

输出结果:

next(😬)
next(😨)
next(😡)
next(🔴)
Error: test
next(😊)

catchErrorJustReturn 拦截错误事件并结束序列

catchErrorJustReturn 操作符会将error 事件替换成其他的一个元素,然后结束该序列。 演示:

let disposeBag = DisposeBag()
let sequenceThatFails = PublishSubject<String>()

sequenceThatFails
    .catchErrorJustReturn("😊")
    .subscribe { print($0) }
    .disposed(by: disposeBag)

sequenceThatFails.onNext("😬")
sequenceThatFails.onNext("😨")
sequenceThatFails.onNext("😡")
sequenceThatFails.onNext("🔴")
sequenceThatFails.onError(TestError.test)

输出结果:

next(😬)
next(😨)
next(😡)
next(🔴)
next(😊)
completed

retry 错误了重试

如果源 Observable 产生一个错误事件,重新对它进行订阅,希望它不会再次产生错误。
retry 操作符将不会将 error 事件,传递给观察者,然而,它会从新订阅源 Observable,给这个 Observable 一个重试的机会,让它有机会不产生 error 事件。retry 总是对观察者发出 next 事件,即便源序列产生了一个 error 事件,所以这样可能会产生重复的元素。 演示:

let disposeBag = DisposeBag()
var count = 1

let sequenceThatErrors = Observable<String>.create { observer in
    observer.onNext("🍎")
    observer.onNext("🍐")
    observer.onNext("🍊")

    if count < 5 {
        observer.onError(TestError.test)
        print("Error encountered")
        count += 1
    }

    observer.onNext("🐶")
    observer.onNext("🐱")
    observer.onNext("🐭")
    observer.onCompleted()

    return Disposables.create()
}

sequenceThatErrors
    .retry(3)
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

输出:

🍎
🍐
🍊
Error encountered
🍎
🍐
🍊
Error encountered
🍎
🍐
🍊
Error encountered
Unhandled error happened: test
 subscription called from:

.retry(3)表示重复3次,.retry()表示无限重复

timeout 超时就错误

如果源 Observable 在规定时间内没有发出任何元素,就产生一个超时的 error 事件

debug

打印所有的订阅,事件以及销毁信息
演示:

let disposeBag = DisposeBag()
let sequence = Observable<String>.create { observer in
    observer.onNext("🍎")
    observer.onNext("🍐")
    observer.onCompleted()
    return Disposables.create()
}
sequence
    .debug("Fruit")
    .subscribe()
    .disposed(by: disposeBag)

输出结果:

2019-12-23 15:42:47.864: Fruit -> subscribed
2019-12-23 15:42:47.872: Fruit -> Event next(🍎)
2019-12-23 15:42:47.872: Fruit -> Event next(🍐)
2019-12-23 15:42:47.872: Fruit -> Event completed
2019-12-23 15:42:47.872: Fruit -> isDisposed

do

当 Observable 的某些事件产生时,你可以使用 do 操作符来注册一些回调操作。这些回调会被单独调用,它们会和 Observable 原本的回调分离。 演示:

let disposeBag = DisposeBag()

Observable.of(1,2,3)
    .do(onNext: { (x) in
        print("onNext : \(x)")
    }, afterNext: { (x) in
        print("afterNext : \(x)")
    })
    .subscribe(onNext:{print($0)})
    .disposed(by: disposeBag)

输出:

onNext : 1
1
afterNext : 1
onNext : 2
2
afterNext : 2
onNext : 3
3
afterNext : 3

指定Scheduler

observeOn 在指定Scheduler发元素

指定 Observable 在那个 Scheduler 发出通知。

注意⚠️:一旦产生了 onError 事件, observeOn 操作符将立即转发。他不会等待 onError 之前的事件全部被收到。这意味着 onError 事件可能会跳过一些元素提前发送出去。

subscribeOn 在指定Scheduler执行

指示 Observable 在哪个 Scheduler 发出执行。

评估

reduce 最终结果

reduce 接受一个初始值,和一个操作符号。
reduce 将给定的初始值,与序列里的每个值进行累计运算。得到一个最终结果,并将其作为单个值发送出去。
演示:

let disposeBag = DisposeBag()

Observable.of(10, 100, 1000)
    .reduce(1, accumulator: +)
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

输出:

1111

scan 每个结果

scan 操作符将对第一个元素应用一个函数,将结果作为第一个元素发出。然后,将结果作为参数填入到第二个元素的应用函数中,创建第二个元素。以此类推,直到遍历完全部的元素。
演示:

let disposeBag = DisposeBag()
//.scan(1)提供了一个初始值1
Observable.of(10, 100, 1000)
    .scan(1) { aggregateValue, newValue in
        aggregateValue + newValue
    }
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

输出:

11
111
1111

和reduce的区别就在于reduce只发出最终元素

replay

确保观察者接收到同样的序列,即使是在 Observable 发出元素后才订阅。
replay 操作符将 Observable 转换为可被连接的 Observable,并且这个可被连接的 Observable 将缓存最新的 n 个元素。当有新的观察者对它进行订阅时,它就把这些被缓存的元素发送给观察者。
演示:

let intSequence = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
    .replay(5)

_ = intSequence
    .subscribe(onNext: { print("Subscription 1:, Event: \($0)") })

DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
    _ = intSequence.connect()
}

DispatchQueue.main.asyncAfter(deadline: .now() + 4) {
  _ = intSequence
      .subscribe(onNext: { print("Subscription 2:, Event: \($0)") })
}

DispatchQueue.main.asyncAfter(deadline: .now() + 8) {
  _ = intSequence
      .subscribe(onNext: { print("Subscription 3:, Event: \($0)") })
}

输出:

Subscription 1:, Event: 0
Subscription 2:, Event: 0
Subscription 1:, Event: 1
Subscription 2:, Event: 1
Subscription 1:, Event: 2
Subscription 2:, Event: 2
Subscription 1:, Event: 3
Subscription 2:, Event: 3
Subscription 1:, Event: 4
Subscription 2:, Event: 4
Subscription 3:, Event: 0
Subscription 3:, Event: 1
Subscription 3:, Event: 2
Subscription 3:, Event: 3
Subscription 3:, Event: 4
Subscription 1:, Event: 5
Subscription 2:, Event: 5
Subscription 3:, Event: 5
Subscription 1:, Event: 6
Subscription 2:, Event: 6
Subscription 3:, Event: 6
...

shareReplay

使观察者共享 Observable,观察者会立即收到最新的元素,即使这些元素是在订阅前产生的。
shareReplay 操作符将使得观察者共享源 Observable,并且缓存最新的 n 个元素,将这些元素直接发送给新的观察者。

single

限制 Observable 只有一个元素,否出发出一个 error 事件。

  • 如果 Observable 只有一个元素,它将镜像这个 Observable 。
  • 如果 Observable 没有元素或者元素数量大于一,它将产生一个 error 事件。

using

创建一个可被清除的资源,它和 Observable 具有相同的寿命

通过使用 using 操作符创建 Observable 时,同时创建一个可被清除的资源,一旦 Observable 终止了,那么这个资源就会被清除掉了。

withLatestFrom

将两个 Observables 最新的元素通过一个函数组合起来,当第一个 Observable 发出一个元素,就将组合后的元素发送出来

  • 通过函数组合
  • 第一个 Observable才能触发

演示1: 当第一个 Observable 发出一个元素时,就立即取出第二个 Observable 中最新的元素,然后把第二个 Observable 中最新的元素发送出去。

let disposeBag = DisposeBag()
let firstSubject = PublishSubject<String>()
let secondSubject = PublishSubject<String>()

firstSubject
     .withLatestFrom(secondSubject)
     .subscribe(onNext: { print($0) })
     .disposed(by: disposeBag)

firstSubject.onNext("🅰️")
firstSubject.onNext("🅱️")
secondSubject.onNext("1")
secondSubject.onNext("2")
firstSubject.onNext("🆎")

输出:

2

演示2: 当第一个 Observable 发出一个元素时,就立即取出第二个 Observable 中最新的元素,将第一个 Observable 中最新的元素 first 和第二个 Observable 中最新的元素second组合,然后把组合结果 first+second发送出去。

let disposeBag = DisposeBag()
let firstSubject = PublishSubject<String>()
let secondSubject = PublishSubject<String>()

firstSubject
     .withLatestFrom(secondSubject) {
          (first, second) in
          return first + second
     }
     .subscribe(onNext: { print($0) })
     .disposed(by: disposeBag)

firstSubject.onNext("🅰️")
firstSubject.onNext("🅱️")
secondSubject.onNext("1")
secondSubject.onNext("2")
firstSubject.onNext("🆎")

输出:

🆎2

注意⚠️: 第二个 Observable有元素了,才开始打印