12-理解重复订阅Observable的行为
share() - 不要反复订阅同一个Observable
let numbers = Observable.of(1, 2, 3, 4, 5)
_ = numbers.subscribe(onNext: { print($0) })
_ = numbers.subscribe(onNext: { print($0) })
假设,我们希望这两次订阅实际上使用的是同一个Observable,但执行一下就会在控制台看到,打印了两次1 2 3 4 5,也就是说每次订阅,都会产生一个新的Observable
对象,多次订阅的默认行为,并不是共享同一个序列上的事件。
为了在多次订阅的时候共享事件,我们可以使用share operator,为了观察这个效果,我们把numbers的定义改成这样:
let numbers = Observable.of(1, 2, 3, 4, 5).share()
scan operator
_ = selectedPhotos.scan([]) {
(photos: [UIImage], newPhoto: UIImage) in
var newPhotos = photos
if let index = newPhotos.index(where: { newPhoto == $0 }) {
newPhotos.remove(at: index)
}
else {
newPhotos.append(newPhoto)
}
return newPhotos
}.subscribe(onNext: { (photos: [UIImage]) in
self.images.value = photos
}, onDisposed: {
print("Finished choose photo memos.")
})
上述的scan作用类似于集合类型中的reduce
.
13-更好的处理授权提示
iOS的用户授权动作是异步的.为了能在用户完成授权操作之后继续更新UI,我们得先把授权的结果封装成一个Observable
。实际上,这个Observable
只可能是下面三种情况:
* 如果用户已授权,事件序列就是:.next(true)
,.completed()
;
* 如果用户未授权,序列的第一个事件就一定是.next(false)
。然后,如果用户拒绝授权,序列中的事件就是:.next(false)
和.completed
。否则,就是.next(true)
和.completed
;
订阅用户的授权结果
订阅的部分,应该写在PhotoCollectionViewController.viewDidLoad
方法里。先别着急,这个过程要比我们想象的复杂一点,我们不能直接订阅isAuthorized
的onNext
并处理true
/false
的情况,因为单一的事件值并不能反映真实的授权情况。按照之前分析的:
- 授权成功的序列可能是:
.next(true)
,.completed
或.next(false)
,.next(true)
,.completed
; - 授权失败的序列则是:
.next(false)
,.next(false)
,.completed
;
因此,我们需要把isAuthorized
这个事件序列处理一下,分别处理授权成功和失败的情况。
订阅成功事件
首先来订阅授权成功事件,我们只要忽略掉事件序列中所有的false,并读到第一个true,就可以认为授权成功了。使用“过滤型”operator
可以轻松完成这个任务:
override func viewDidLoad() {
super.viewDidLoad()
...
// PHPhotoLibrary.isAuthorized返回一个Observable<Bool>
let isAuthorized = PHPhotoLibrary.isAuthorized
isAuthorized
.skipWhile { $0 == false }
.take(1)
.subscribe(onNext: {
[weak self] _ in
// Reload the photo collection view
})
.addDisposableTo(bag)
}
可以看到, 上面的代码中, 使用skipWhile
忽略了所有false
, 并且读取到了之后第一个true
.
订阅失败事件
接下来,我们处理拒绝授权的情况。这种情况相比成功简单一些,因为它对应的事件序列只有一种情况:.next(false)
,.next(false)
,.completed
。因此,我们只要对事件序列中所有元素去重之后,订阅最后一个.next
事件,如果是false
,就可以确定是用户拒绝授权了。因此,在订阅成功授权的代码后面,继续添加下面的代码:
override func viewDidLoad() {
super.viewDidLoad()
...
isAuthorized
.distinctUntilChanged()
.takeLast(1)
.filter{ $0 == false }
.subscribe(onNext: { [weak self] _ in
self?.flash(title: "Cannot access your photo library",message: "You can authorize access from the Settings.",
callback: { [weak self] _ in
self?.navigationController?.popViewController(animated: true)
}))
})
.addDisposableTo(bag)
}
14-了解常用的transform operators
toArray
toArray
,这可以说是最简单的Transform operator。它把Observable<T>
中所有的事件值,在订阅的时候,打包成一个Array<T>
返回给订阅者.
有一点要注意的是,toArray
的转换,是在订阅的时候,根据当前Observable
中的值一次性完成转换的,后续的事件订阅则不会再进行转换:
let numbers = PublishSubject<Int>()
numbers.asObservable()
.toArray()
.subscribe(onNext: {
print($0)
}).addDisposableTo(bag)
numbers.onNext(1)
numbers.onNext(2)
numbers.onNext(3)
对于这个例子来说,在订阅的时候,使用了toArray
,但此时,numbers
中没有任何值,toArray
变换出来的,就是个空数组。即便之后numbers
中发生了事件123
,但是,我们订阅的,已经不是numbers
,而是numbers
在订阅的时候转换成的Observable<Array<Int>>
,用序列图来表示是这样的:
我们订阅的代码是图中红色的Observable,因此,也就不会打印任何内容了。
scan
Observable.of(1, 2, 3).scan(0) {
accumulatedValue, value in
accumulatedValue + value
}.subscribe(onNext: {
print($0)
}).addDisposableTo(bag)
输出为 1、3、6.但是,就想上面图中展示的那样,和toArray
不同的是,scan
在Observable
每次有事件的时候都会执行.
转换事件类型的map
Observable.of(1, 2, 3).map {
value in value * 2
}.subscribe(onNext: {
print($0)
}).addDisposableTo(bag)
15-为什么RxSwift也需要flatMap
flatMap
在集合
中使用到, 在Optional
中也使用到, 到了RxSwift
也用到它, 但是在这些不同的领域中, flatMap
往往又表示了不同的含义. 在RxSwift
中是这么定义flatMap
的:
Transform the items emitted by an Observable into Observables, then flatten the emissions from those into a single Observable.
翻译过来就是 将由Observable
发送来的Item
转换成一个个Observables
, 然后再展开, 将所有Observables
的发射序列合并成一个Observable
.
把序列中的事件变成新的Observable
来看下RxSwift官方demo中提供的例子:
struct Player {
var score: Variable<Int>
}
let 👦🏻 = Player(score: Variable(80))
let 👧🏼 = Player(score: Variable(90))
然后创建一个Variable
, 然后订阅它
let player = Variable(👦🏻)
player.asObservable()
.flatMap { $0.score.asObservable() } // Change flatMap to flatMapLatest and observe change in printed output
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
👦🏻.score.value = 85 // 输出 80 85
👦🏻是Player
序列中发生的事件, 通过flatMap
我们把它变成了一个Observable<Int>
, 这就是flatMap
定义的前半句话的含义: Transform the items emitted by an Observable into Observables.
合并所有Observables
的发射序列
接下来, 我们把👧🏼
加入, 注意下👧🏼
添加的位置
player.value = 👦🏻 // 输出80
👦🏻.score.value = 85 // 输出85
player.value = 👧🏼 // 输出90
👧🏼.value = 95 // 输出95