[RxSwift] #7) Driver๋ ๋ฌด์์ธ๊ฐ?? [Driver์ Relay]
๋ณธ ํฌ์คํ ์ ๊ณฐํ๊น๋์ ๊ฐ์์์์ ๊ธฐ๋ฐ์ผ๋ก, ๊ฐ์ธ์ ์ผ๋ก ๊ณต๋ถํ ๋ด์ฉ์ ์ ๋ฆฌํ ๊ธ์ ๋๋ค.
๋์ฑ ์์ธํ ๋ด์ฉ์, ๊ฐ์ ์์์ ์ง์ ์์ฒญํ์๋๊ฒ์ ์ถ์ฒ๋๋ฆฝ๋๋ค!
์ด๋ฒ ๊ธ์์๋ ๊ฐ๋จํ๊ฒ Driver๊ฐ ๋ฌด์์ธ์ง์ ๋ํด์ ๊ฐ๋จํ๊ฒ ์ ๋ฆฌ ํด ๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค!
๊ธ ๋ง๋ฌด๋ฆฌ์, 5์ค์์ฝ์ผ๋ก ๊ฐ๋จํ๊ฒ ์ ๋ฆฌ ํ ๋ด์ฉ๋ ์์ผ๋ ์๊ฐ์ด ์์ผ์ ๋ถ๋ค์ ์ฐธ๊ณ ํด์ฃผ์ธ์.
์ด์ ํฌ์คํ ์์ RxCocoa๋ฅผ ์ค๋ช ํ ๋ 'UIKit์ Rx์์๋ค์ ์ ์ฉ์ํจ Extentsion'์ด๋ผ๊ณ ์ค๋ช ํ์ต๋๋ค.
์๋ฅผ ๋ค๋ฉด, ์๋์ ๊ฐ์ ์ฝ๋๊ฐ RxCocoa๋ฅผ ํ์ฉํ์ฌ UIKit์ ์ง์ ์ ์ผ๋ก Observable์ ๋ฐ์ธ๋ฉ ํ ์์์ ๋๋ค.
viewModel.itemsCount
.map{"\($0)"}
.observeOn(MainScheduler.instance)
.bind(to: itemCountLabel.rx.text)
.disposed(by: disposeBag)
๊ทธ๋ฐ๋ฐ ์ด๋ฌํ 1) UI์ ์ง์ ๋ฐ์ธ๋ฉํ๋ ์์ ์ ๊ฒฝ์ฐ์๋ ๋ฐ๋์ Main-Thread(UI Thread)์์ ๋์์ ํด์ผํ๋ค๋ ํน์ง์ด ์์ต๋๋ค.
๊ทธ๋ ๊ธฐ ๋๋ฌธ์, .bind()๋ฅผ ํ๊ธฐ ์ ์ .observeOn(MainScheduler.instance)๋ฅผ ํตํด์
๊ฐ์ ๋ก ํด๋น stream์ด Main-Thread์์ ์งํ๋๋๋ก ํด ์ฃผ๋ ๊ฒ์ ๋๋ค.
๋ฐ๋ผ์, .bind()๋ฅผ ์ฌ์ฉํ ๋ ํญ์ .observeOn(MainScheduler.instance)๋ฅผ ๊ฐ์ด ์์ฑ ํด ์ค์ผํ๋ค๋ ๊ท์ฐฎ์ ์ ์ด ์์ต๋๋ค.
๋ํ UI๋์์ ๊ฒฝ์ฐ๋ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๋ ๋์ค Error๊ฐ ๋ฐ์ํ๋ฉด ๊ทธ ์๊ฐ Stream์ด ๋์ด์ ธ ๋ฒ๋ฆฌ๊ณ ,
ํด๋น Stream์ ํ๋ฒ ์ข ๋ฃ๋๋ฉด ๋์ด์ ์ฌ์ฌ์ฉ์ด ๋ถ๊ฐ๋ฅํฉ๋๋ค.
์ด๊ฒ์ ์ฐ๋ฆฌ๊ฐ UI๋์์ ํ๋ค๊ฐ Error๊ฐ ๋ฐ์ํ๋ฉด ์ด UI๋ ๋์ด์ ๋์ํ์ง ๋ชปํ๋ค๋ ๊ฒ์ ์๋ฏธํ๋๋ฐ,
์ด๋ ๊ฒ 2) UI๊ฐ Error๋ฅผ ๋ง์ฃผํ์ ๋, Stream์ด ๊ทธ๋๋ก ์ข ๋ฃ๋๋ ๊ฒ์ ๋ฐฉ์งํ๊ธฐ ์ํด์
.catchErrorJustReturn()์ด๋ผ๋ operator๋ฅผ ์ ๊ณตํฉ๋๋ค.
viewModel.itemsCount
.map{"\($0)"}
.catchErrorJustReturn("")
.observeOn(MainScheduler.instance)
.bind(to: itemCountLabel.rx.text)
.disposed(by: disposeBag)
์ ์ฝ๋์ .catchErrorJustReturn("")์ ํตํด์, Error๊ฐ ๋ฐ์ํ๋ฉด ๊ทธ๋๋ก ๋น ๋ฌธ์์ด์ text๋ก ์ ๋ฌํ๊ฒ ๋ฉ๋๋ค.
์ ๊ทธ๋ ๋ค๋ฉด, Rx๋ฅผ UI์ ์ ์ฉํ๊ธฐ ์ํด ์ฐ๋ฆฌ๋ 2๊ฐ์ง์ Operator๊ฐ ๋ฐ๋์ ํ์ํ๋ค๋ ๊ฒ์ ์ ์ ์์ต๋๋ค.
1) .catchErrorJustReturn()
2) .observeOn(MainScheduler.instance)
ํ์ง๋ง, RxSwift๋ ์ด๋ฌํ 2๊ฐ์ง์ operator๊ฐ ํ๋ ๊ธฐ๋ฅ๋ค์ 1๊ฐ์ operator๋ก ์ ๊ณต ํด ์ฃผ๋๋ฐ
๊ทธ๊ฒ์ด ๋ฐ๋ก .asDriver()์ ๋๋ค.
๊ทธ๋ฆฌ๊ณ ์ด .asDriver()๋ .bind() ๋์ , .drive()๋ผ๋ Operator์ ํจ๊ป ์ฌ์ฉ๋ฉ๋๋ค.
์์์ ๋ณด์ฌ๋๋ฆฐ ์ฝ๋๋ ์๋์ ๊ฐ์ด ๋ณ๊ฒฝ๋ฉ๋๋ค.
viewModel.itemsCount
.map{"\($0)"}
.asDriver(onErrorJustReturn: "")
.drive(itemCountLabel.rx.text)
.disposed(by: disposeBag)
์์ ์ฝ๋๋ฅผ ํด์ํด๋ณด๋ฉด,
[Driver๋ก์จ ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด๋๋ฐ, ์๋ฌ๊ฐ ๋ฐ์ํ๋ฉด ๋น ๋ฌธ์์ด("")์ ๋๋ผ์ด๋ธ(๋ฐ์ธ๋ฉ)ํ๊ณ ์๋๋ผ๋ฉด ๊ฐ์ ๋๋ผ์ด๋ธ(๋ฐ์ธ๋ฉ) ํ ๊ฒ!]
๋ผ๊ณ ํด์ํ ์ ์์ต๋๋ค.
์ฆ, Driver๋ Observable์ ๊ธฐ๋ฅ์ ํ์ง๋ง UI๋์์ ํนํ๋ Observable์ด๋ผ๊ณ ์๊ฐํ์๋ฉด๋ฉ๋๋ค!
๊ทธ๋ ๊ธฐ ๋๋ฌธ์, ์๋์ ๊ฐ์ด ์ ์ธ๋ ๊ฐ๋ฅํฉ๋๋ค!
var ์ผ๋ฐ์ ์ธ_Observable : Observable<[Menu]>
var UI์_ํนํ๋_Observavle : Driver<[Menu]>
์ ๊ทธ๋ ๋ค๋ฉด, ๋ญ๊ฐ Observable๋ฟ ์๋๋ผ, Subject๋ํ ์ด๋ฌํ ๋์ผํ ์ด์ ๋ก UI์์ ์ ์ํ ํน๋ณํ ๋ฌด์ธ๊ฐ๊ฐ ์์ง ์์๊น์??
๋ฐ๋ก ๊ทธ๊ฒ์ Relay๋ผ๊ณ ๋ถ๋ฆ ๋๋ค!
๋ฐ๋ผ์ ์ฐ๋ฆฌ๊ฐ ๊ธฐ์กด์ ์ฌ์ฉํ๋ BehaviorSubject๋ฅผ BehaviorRelay๋ก ๋ฐ๊ฟ์ฃผ๋ฉด
Error์ฒ๋ฆฌ๋, Completed๋๋ ์ํฉ์ ๋ํ ์ ๊ฒฝ์ ์ฐ์ง ์์๋ ๋๊ธฐ ๋๋ฌธ์
.onNext() Operator๊ฐ .accept() Operator๋ก ๋์ฒด๋๊ฒ ๋ฉ๋๋ค!
class MenuListViewModel{
var menusObservable = BehaviorSubject<[Menu]>(value: [])
func clearAllItemSelections(){
menusObservable
.map {
$0.map{ m in
Menu(id: m.id, name: m.name, price: m.price, count: 0)
}
}
.take(1)
.subscribe (onNext:{
self.menusObservable.accept($0)
// self.menusObservable.onNext($0)
// ํด๋น ๋ถ๋ถ์์ .onNext($0) -> .accept($0)๋ก ๋ณ๊ฒฝ ๋จ
})
}
}
<5์ค ์์ฝ>
1. UI๋์์ Main Thread(UI Thread)์์๋ง ๋์๊ฐ๊ณ , Error๊ฐ ๋ฐ์ํ๋ฉด Stream์ด ๋ฐ๋ก ๋์ด์ง๋ ๊ฒ์ ๋ฐฉ์งํด์ผ ํจ.
2. ์ด๋ฅผ ์ํด, Operator๋ก .asDriver()๋ฅผ ์ ๊ณตํ๊ณ .bind()๋์ .drive()์ ํจ๊ป ์ฌ์ฉํจ.
3. ์ฆ, Driver๋ UI์์ ์ ํนํ๋ Observable์ ์ข ๋ฅ์.
4. Driver์ ๋์ผํ ์ด์ ๋ก Subject์์ ํ์๋ ๊ฒ์ด Relay์.
5. ์ด๋ฌํ Relay๋ .onNext() ๋์ .accept()๋ฅผ ์ฌ์ฉํจ. ์๋ํ๋ฉด Error๋ Completed๋ฅผ ๊ณ ๋ คํ์ง ์์๋ ๋๊ธฐ ๋๋ฌธ์.
+
Subject<T>๋ก ๋ฐฉ์ถํ ๊ฐ์ด, ํน์ stream์์ UI์์ ์ผ๋ก ์งํ๋๋ค๋ฉด .asDriver()๋ฅผ ์ฌ์ฉํ์ฌ
ํด๋น ๊ตฌ๊ฐ์์๋ง Observable์ Driver๋ก ๋ฐ๊ฟ์ค๋ค๊ณ ์๊ฐํ๋ฉด ๋ฉ๋๋ค.
ํ์ง๋ง, ์ค๊ฐ์ ๋ฐ์ดํฐ ๊ฐ์ ๋ณํ ์์ด ์ฒ์๋ถํฐ UI์ ์ฐ๊ฒฐ ํ ๋ชฉ์ ์ด๋ฉด Driver<T>๋ก Observable์ ์์ฑํด๋ ๋๋ค.
(๊ทธ๋ฐ๋ฐ ์ฌ๋ฌ ์ฝ๋๋ฅผ ์ดํด ๋ณธ ๊ฒฐ๊ณผ, ์ผ๋จ ๊ฐ์ฅ ๋ง๋งํ(?) Subject๋ก ์์ฑํด๋๊ณ , ํ์ํ Stream์์ .asDriver()๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ ๊ฒ ๊ฐ์ต๋๋ค....! )