iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 10
0
Mobile Development

RxSwift / 30天探索之旅系列 第 10

第 10 天 - Transforming Observables (上)

嗨,大家好,今天介紹Transforming,都是很常用到的,應該說幾乎不可能用不到,我們就開始吧!

Map

定義『將元素轉換』,這就跟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

flatMap

定義『把Observable的每個元素個別投影到一個Observable,併合併每一個結果到一個Observable』

自己比較常用到有兩種情境,一個是『Observable的元素有其他Observable 』、另一個是『Call API』

Observable的元素有其他Observable

為了展現flatMap特性,可以搭配下方程式跟圖片,那我們開始講解範例了!

假設我們有一個struct,其中,amount是BehaviorRelay<Int>

struct Product {
    let name: String
    let amount: BehaviorRelay<Int>
}

今天我們把建立了iPhone11跟iPhoneSE,因為amount是Observable,我們嘗試用flatMap取得到amount

  1. 讓Subject接收iPhone11跟iPhoneSE,會將『每個元素個別投影到一個Observable』,也就是圖片藍色的部分,把iPhone11的amount(1)投影到一個Observable,並發出元素1,iPhoneSE做了也一樣的事情
  2. 將『每個投影的Observable的結果,在合併到另一個Observable』,也就是圖片紅色的部分
  3. 這時iPhone11或iPhoneSE的amount Observable收到新的元素,投影後的Observable也會收到,並且合併後的Observable也會收到,就像圖片中的元素3跟0。
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
*/ 

https://ithelp.ithome.com.tw/upload/images/20200924/20115751V5jghDI2AZ.png

Call API

假設我們有一個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(()) 
}
  1. subject.flatMap { API }並且訂閱
  2. 接著當Subject收到元素,就會觸發(trigger)API
  3. 一秒後再觸發一次
  4. 兩秒後再觸發一次

執行結果如下,可以看到 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(())

flatMapLatest

定義『把Observable的每個元素個別投影到一個Observable ,併合併每一個結果到一個Observable,並且,只取 Observable最新元素所投影出來的結果』。

下面我們就將上面flatMap的程式,直接改成flatMapLatest,來看看有怎樣的結果吧!

Observable的元素有其他Observable

在這範例中,我們修改成

subject
    .flatMapLatest { $0.amount }

執行結果

1
2
0

會發現3不見了,這是因為當subject.onNext(iPhoneSE)的時候,對subject 來說,最新的元素是 iPhoneSE,而不是iPhone11,所以接下來的iPhone11.amount.accept(3)並不會將3合併到結果上,畫成圖會長這樣
https://ithelp.ithome.com.tw/upload/images/20200924/20115751535mMWhU2L.png

Call API

假設一樣使用上面的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,最終,只會回傳第三次的結果。

使用時機

有了以上範例,應該對於flatMapflatMapLatest有比較清楚的了解吧?通常我們在呼叫API要得到資料的時候,是會用flatMapLatest,但今天如果你是要傳遞資料回Server,並且多筆多次,也許flatMap是不錯選擇哦!


今天講了Transforming,都是常用到,但也常用錯的,我剛開始學時都搞不懂,希望能給跟我一樣不懂得人一點幫助,明天繼續講Transforming,掰掰


上一篇
第 9 天 - Filtering Observables
下一篇
第 11 天 - Transforming Observables(下)
系列文
RxSwift / 30天探索之旅30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言