嗨,大家好,今天介紹Transforming,都是很常用到的,應該說幾乎不可能用不到,我們就開始吧!
定義『將元素轉換』,這就跟Swift 原生的map是一樣的,通過closure內定義要做的事情,進行轉換,可以Int轉Double,Void轉成需要夾帶的資料之類的,是很常很常用到的一種operator。
這裡擷取RxSwift: Reactive Programming with Swift書中,該章節的範例,因為覺得滿有趣的。透過 NumberFormatter(),將數字轉成英文拼寫
let subject = PublishSubject<Int>()
let formatter = NumberFormatter()
formatter.numberStyle = .spellOut
subject.map { formatter.string(from: NSNumber(value: $0)) ?? "" }
.subscribe(onNext: {
print($0)
})
.disposed(by: disposeBag)
subject.onNext(101)
subject.onNext(5)
subject.onNext(67)
執行結果
one hundred one
five
sixty-seven
定義『把Observable的每個元素個別投影到一個Observable,併合併每一個結果到一個Observable』
自己比較常用到有兩種情境,一個是『Observable的元素有其他Observable 』、另一個是『Call API』
為了展現flatMap
特性,可以搭配下方程式跟圖片,那我們開始講解範例了!
假設我們有一個struct,其中,amount是BehaviorRelay<Int>
struct Product {
let name: String
let amount: BehaviorRelay<Int>
}
今天我們把建立了iPhone11跟iPhoneSE,因為amount是Observable,我們嘗試用flatMap
取得到amount
let iPhone11 = Product(name: "iPhone 11", amount: BehaviorRelay<Int>(value: 1))
let iPhoneSE = Product(name: "iPhone SE", amount: BehaviorRelay<Int>(value: 2))
let subject = PublishSubject<Product>()
subject
.flatMap { $0.amount }
.subscribe(onNext: {
print($0)
})
.disposed(by: disposeBag)
subject.onNext(iPhone11) // 1, 2
subject.onNext(iPhoneSE) // 1, 2
iPhone11.amount.accept(3) // 3
iPhoneSE.amount.accept(0) // 3
/*
執行結果:
1
2
3
0
*/
假設我們有一個API,呼叫後可以收到回傳的結果,那當我們『希望每次呼叫API都是獨立的,我也預期我會得到每次的結果』,這時你就可以使用flatMap
(如果你只想要取得最新的結果,這要用 flatMapLatest ,下面會講)
先建立一個假的 API,呼叫request()
後,等個兩秒,就會回應一個Single,並且只會回傳.next
事件。
補充說明
Single之前先跳過沒講,可以想成Single就是一種變化型的Observable,他只會回傳.next(element)
或是.error(Error)
,也就是『成功得到值,或失敗』的概念,少了原先Observable的.completed
事件,像是 API這種一次性的,用Single搭配materialize
滿舒服的,這要明天才會講到。
class API {
func request() -> Single<Void> {
.create { (single) -> Disposable in
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
single(.success(()))
}
return Disposables.create()
}
}
}
接著看看範例程式
let subject = PublishSubject<Void>()
// 1
subject.flatMap { API().request() }.debug("Call API").subscribe().disposed(by: disposeBag)
// 2
subject.onNext(())
// 3
delay(1) {
subject.onNext(())
}
// 4
delay(2) {
subject.onNext(())
}
subject.flatMap { API }
並且訂閱執行結果如下,可以看到 API 確確實實被呼叫了三次
45:56.823: Call API -> subscribed
45:58.827: Call API -> Event next(())
45:59.827: Call API -> Event next(())
46:00.830: Call API -> Event next(())
定義『把Observable的每個元素個別投影到一個Observable ,併合併每一個結果到一個Observable,並且,只取 Observable最新元素所投影出來的結果』。
下面我們就將上面flatMap
的程式,直接改成flatMapLatest
,來看看有怎樣的結果吧!
在這範例中,我們修改成
subject
.flatMapLatest { $0.amount }
執行結果
1
2
0
會發現3不見了,這是因為當subject.onNext(iPhoneSE)
的時候,對subject 來說,最新的元素是 iPhoneSE,而不是iPhone11,所以接下來的iPhone11.amount.accept(3)
並不會將3合併到結果上,畫成圖會長這樣
假設一樣使用上面的API ,我們『預期呼叫多次,只要得到最後一次的結果』,就好比,你有一個按鈕,點下去就呼叫一次API,可是有一種人就是看到按鈕就很想多按幾下,又如果這時你是用flatMap
,會怎樣呢?你會不停的去呼叫 API,那讓我們看看換成flatMapLatest
結果會長怎樣
subject.flatMapLatest{ API().request() }.debug("Call API").subscribe().disposed(by: disposeBag)
執行結果
48:25.850: Call API -> subscribed
48:30.048: Call API -> Event next(())
因為我們設定API要執行兩秒才會回傳,當呼叫第一次,還沒回傳成功時,我們下一秒又在呼叫一次API,這時候第一次呼叫會被disposed,換第二次的繼續執行,當我們下一秒又呼叫第三次API,第二次就會被disposed,最終,只會回傳第三次的結果。
有了以上範例,應該對於flatMap
跟flatMapLatest
有比較清楚的了解吧?通常我們在呼叫API要得到資料的時候,是會用flatMapLatest
,但今天如果你是要傳遞資料回Server,並且多筆多次,也許flatMap
是不錯選擇哦!
今天講了Transforming,都是常用到,但也常用錯的,我剛開始學時都搞不懂,希望能給跟我一樣不懂得人一點幫助,明天繼續講Transforming,掰掰