์๋ ํ์ธ์ ์ ์ธ์ ๋๋ค :)
์ง๋ ๊ฒ์๊ธ์ ์ด์ด ์ด๋ฒ ๊ฒ์๊ธ์์๋ RxCocoa์ Traits์ ๋ํด ์ ๋ฆฌํด๋ณด๊ฒ ์ต๋๋ค.
์ด๋ฒ ๊ฒ์๊ธ๋ ReactiveX/RxSwift์ Traits ๋ฌธ์ ๋ฒ์ญ์ ํตํด ์์ฑํ๊ฒ ์ต๋๋ค!
RxCocoa Traits์ ์ข ๋ฅ
RxCocoa์ Traits๋ก๋ Driver, Signal, Control Property/ControlEvent๊ฐ ์์ต๋๋ค.
ํ๋์ฉ ์ดํด๋ณด๊ฒ ์ต๋๋ค!
Driver
Driver๋ ๊ฐ์ฅ ์ ๊ตํ trait์ ๋๋ค. UI ๊ณ์ธต์์ reactive ์ฝ๋๋ฅผ ์์ฑํ๋ ์ง๊ด์ ์ธ ๋ฐฉ๋ฒ์ ์ ๊ณตํ๊ฑฐ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ๋(driving)ํ๋ ๋ฐ์ดํฐ ์คํธ๋ฆผ์ ๋ชจ๋ธ๋งํ๋ ค๋ ๋ชจ๋ ๊ฒฝ์ฐ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
+ Driver๋ผ๋ ์ด๋ฆ์ด ๋ถ์ ์ด์ ? Driver์ ๋ชฉ์ ์์ฒด๊ฐ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ๋(drive)ํ๋ ์ํ์ค๋ฅผ ๋ง๋๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ
Driver์ ํน์ง
1. ์๋ฌ๋ฅผ ๋ฐฉ์ถํ์ง ์์ต๋๋ค.
์ํ์ค ์๋ฌ๊ฐ ๋ฐ์ํ๊ฒ ๋๋ฉด ์ ํ๋ฆฌ์ผ์ด์ ์ด ์ฌ์ฉ์ ์ ๋ ฅ์ ๋ํ ์๋ต์ ๋ฉ์ถ๊ฒ ๋ฉ๋๋ค.
2. observe๋ Main Scheduler์์ ๋ฐ์ํฉ๋๋ค.
UI ์์๋ค๊ณผ ๋ก์ง์ ์ผ๋ฐ์ ์ผ๋ก thread safe ํ์ง ์๊ธฐ ๋๋ฌธ์ ์ด๋ฌํ ์์๋ค์ด ๋ฉ์ธ ์ค๋ ๋์์ ๊ด์ฐฐ๋๋ ๊ฒ์ ๋งค์ฐ ์ค์ํฉ๋๋ค.
3. ๋ถ์์์ฉ์ ๊ณต์ ํฉ๋๋ค.(share(replay: 1, scope: .whileConnected))
Driver๋ ๋ถ์์์ฉ์ ๊ณต์ ํ๋ observable ์ํ์ค๋ฅผ ๋ง๋ญ๋๋ค.
share(replay: , scope: )
ํ๋ฒ ์์ฑํ ์ํ์ค๋ฅผ ๊ณต์ ํด ์ฌ์ฉํ ์ ์๋๋ก ํ๋ operator์ ๋๋ค. share๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฐ์ํ ์ด๋ฒคํธ๊ฐ ๋ฒํผ์ ์ ์ฅ๋๊ณ , ์๋ก์ด subscription์ ์๋ก์ด ๋ฐ์ดํฐ ์ํ์ค๋ฅผ ์์ฑํ๋ ๊ฒ์ด ์๋๋ผ ๋ฒํผ์ ์ ์ฅ๋ ์ด๋ฒคํธ๋ฅผ ์ ๋ฌ๋ฐ๊ฒ ๋ฉ๋๋ค.
- replay: ๋ฒํผ ์ฌ์ด์ฆ(์ ๊ท subscriber์๊ฒ ์ด์ ๋ฐฉ์ถํ๋ ์์๋ฅผ ๋ช ๊ฐ๋ฅผ ๊ธฐ๋กํ๋ค๊ฐ ๋ฐฉ์ถํ (replay) ๊ฒ์ธ๊ฐ)
- scope
1) .whileConnected: subscriber๊ฐ ํ ๊ฐ ์ด์ ์์ผ๋ฉด replay๊ฐ ์ ์ง๋์ง๋ง, dispose๋์ด 0๊ฐ๊ฐ ๋๋ฉด replay ๋ฒํผ ์ด๊ธฐํ
2) .forever: subscriber๊ฐ ์์ด๋ ๋ฒํผ ์ ์ง
์์ ์ฝ๋
let results = query.rx.text
.throttle(.milliseconds(300), scheduler: MainScheduler.instance)
.flatMapLatest { query in
fetchAutoCompleteItems(query)
}
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)
์์ ์ฝ๋์ ์๋๋ ๋์์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
1. ์ฌ์ฉ์ input ์กฐ์ (throttle)
throttle operator
throttle์ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ๋ ์ต์ ๊ฐ๊ฒฉ์ ์ ํํ ์ ์๋ operator์ ๋๋ค.
์ง์ ๋ ๊ธฐ๊ฐ(dueTime ์ค์ ) ์์ ์๋ง์ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํด๋, 2๊ฐ ์ด์ ์์๊ฐ ๋ฐฉ์ถ๋์ง ์๋๋ก ํฉ๋๋ค.
2. ์๋ฒ ์์ฒญ์ ํตํด ์ฌ์ฉ์ ๊ฒฐ๊ณผ ๋ชฉ๋ก์ ๊ฐ์ ธ์ค๊ธฐ (์ฟผ๋ฆฌ๋น ํ ๋ฒ)
3. ๊ฒฐ๊ณผ๋ฅผ ๋ UI ์์์ ๋ฐ์ธ๋ฉ (reulstCount, resultsTableView)
์ด ์ฝ๋์ ๋ฌธ์ ์ ์ ๋ฌด์์ผ๊น์?
- ๋ง์ฝ fetchAutoCompleteItems ์ํ์ค์์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉด, ์ค๋ฅ๋ ๋ชจ๋ ๋ฐ์ธ๋ฉ์ ํด์ ํ๊ณ UI๊ฐ ๋ ์ด์ ์ ์ฟผ๋ฆฌ์ ์๋ตํ์ง ์๊ฒ ๋ฉ๋๋ค.
- fetchAutoCompleteItems๊ฐ ์ผ๋ถ ๋ฐฑ๊ทธ๋ผ์ด๋ ์ค๋ ๋์์ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๋ฉด ๊ฒฐ๊ณผ๊ฐ UI ์์์ ๋ฐ์ธ๋ฉ๋๋ ๊ณผ์ ์์ ์ถฉ๋์ด ๋ฐ์ํ ์ ์์ต๋๋ค.
- ๊ฒฐ๊ณผ๋ฅผ ๋ ๊ฐ์ UI ์์์ ๋ฐ์ธ๋ฉํ๋ฉด์, ๊ฐ์ ์ฟผ๋ฆฌ์ ๋ํด ๋ ๊ฐ์ HTTP ์์ฒญ์ ์ํํ๊ฒ ๋ฉ๋๋ค. (์๋๋ ๋์์ด ์๋)
๋ค์๊ณผ ๊ฐ์ด ๊ฐ์ ํ์ฌ ์์ ๋ฌธ์ ์ ๋ค์ ํด๊ฒฐํ ์ ์์ต๋๋ค.
let results = query.rx.text
.throttle(.milliseconds(300), scheduler: MainScheduler.instance)
.flatMapLatest { query in
fetchAutoCompleteItems(query)
.observeOn(MainScheduler.instance) // results are returned on MainScheduler
.catchErrorJustReturn([]) // in the worst case, errors are handled
}
.share(replay: 1) // HTTP requests are shared and results replayed
// to all UI elements
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)
์ฒซ๋ฒ์งธ ์ฝ๋์์ ์ฐจ์ด์ ์ ๋ณด๋ฉด,
.observeOn(MainScheduler.instance) ๋ฅผ ํตํด ๋ฉ์ธ ์ค์ผ์ค๋ฌ์์ ์์ ์ด ์ํ๋๋๋ก ํ๊ณ , .catchErrorJustReturn()์ ํตํด ์๋ฌ ์ํฉ์ ๋๋น(error ๋ฐ์ ์ [] ์ ๋ฌ)ํฉ๋๋ค.
๋ํ, .share(replay: 1) ์ ํตํด ์์ฑ๋ ์ํ์ค๋ฅผ ๊ณต์ ํ ์ ์๋๋ก ํด์ค๋๋ค.
์์ ๊ฐ์ ๋ฐฉ๋ฒ์ผ๋ก ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์์ง๋ง, ๋๊ท๋ชจ ์์คํ ์์ ์์ ๊ฐ์ ์ฌํญ๋ค์ด ์ ์ ํ๊ฒ ์ฒ๋ฆฌ๋๋์ง ํ์ธํ๋ ๊ฒ์ ์ด๋ ค์ธ ์ ์์ต๋๋ค. -> Driver๋ฅผ ์ฌ์ฉํ์ฌ ๋ ๊ฐ๋จํ๊ฒ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
let results = query.rx.text.asDriver() // This converts a normal sequence into a `Driver` sequence.
.throttle(.milliseconds(300), scheduler: MainScheduler.instance)
.flatMapLatest { query in
fetchAutoCompleteItems(query)
.asDriver(onErrorJustReturn: []) // Builder just needs info about what to return in case of error.
}
results
.map { "\($0.count)" }
.drive(resultCount.rx.text) // If there is a `drive` method available instead of `bind(to:)`,
.disposed(by: disposeBag) // that means that the compiler has proven that all properties
// are satisfied.
results
.drive(resultsTableView.rx.items(cellIdentifier: "Cell")) { (_, result, cell) in
cell.textLabel?.text = "\(result)"
}
.disposed(by: disposeBag)
์์ ์ฝ๋์์๋,
.asDriver()๋ฅผ ํตํด ControlProperty trait๋ฅผ Driver trait๋ก ๋ณํํด์ฃผ์์ต๋๋ค. (๋ชจ๋ observable sequence๋ Driver๋ก ๋ณ๊ฒฝ๊ฐ๋ฅ)
.asDriver(onErrorJustReturn: []) ๋ฅผ ํตํด ์๋ฌ ์ฒ๋ฆฌ๋ฅผ ํด์ฃผ์์ต๋๋ค.
.bind(to:) ๋์ drive()๋ฅผ ์ฌ์ฉํด UI์์์ ์ ์ฉํด์ฃผ์์ต๋๋ค.
drive๋ Driver์ ์ ์๋ ๋ฉ์๋๋ก, drive๋ฅผ ์ฌ์ฉํ๋ฉด ํด๋น observable ์ํ์ค๋ ์ ๋ ์ค๋ฅ๋ฅผ ๋ฐฉ์ถํ์ง ์์ผ๋ฉฐ UI ์์์ ๋ฐ์ธ๋ฉํ๊ธฐ์ ์์ ํ ๋ฉ์ธ ์ค๋ ๋์์ ๊ด์ฐฐ๋ฉ๋๋ค.
Signal
Signal์ Driver์ ์ ์ฌํ์ง๋ง ๊ตฌ๋ ์ ์ต์ ์ ์ด๋ฒคํธ๋ฅผ replay ํ์ง ์๋๋ค๋ ์ฐจ์ด์ ์ด ์์ต๋๋ค.
(Driver๋ ๊ตฌ๋ ํ๋ ์๊ฐ ์ต์ ์ ์ด๋ฒคํธ๋ฅผ replay, Signal์ ๊ตฌ๋ ํ ์ดํ์ ๊ฐ๋ง์ ๋ฐฉ์ถ)
Signal์ ํน์ง
- ์๋ฌ๋ฅผ ๋ฐฉ์ถํ์ง ์์
- Main Scheduler์์ ์ด๋ฒคํธ๋ฅผ ๋ฐฉ์ถ
- ๋ถ์์์ฉ์ ๊ณต์ (share(scope: .whileConnected) but Driver์ ๋ค๋ฅด๊ฒ ๋ฒํผ๋ฅผ ๊ฐ์ง์ง ์์)
- ๊ตฌ๋ ์ด์ ์ event๋ฅผ replay ํ์ง ์์
ControlProperty / ControlEvent
ControlProperty
UI ์์์ property๋ฅผ ๋ํ๋ด๋ Observable/ObservableType ์ ์ํ Trait์ ๋๋ค.
ControlProperty ์ํ์ค๋ ์ค์ง ์ต์ด์ control value์ ์ฌ์ฉ์๊ฐ ์์ํ ๊ฐ ๋ณ๊ฒฝ๋ง์ ๋ํ๋ ๋๋ค. (์ฝ๋๋ก ๋ณ๊ฒฝ๋ value change๋ ๊ฐ์งํ์ง ๋ชปํจ)
ControlProperty์ ํน์ง
- ์ ๋ ์คํจํ์ง ์์
- share(replay: 1) ์ฒ๋ผ ๋์ - subscribe ํธ์ถ ์ ๋ง์ง๋ง element๊ฐ ์์ฑ๋๋ ์ฆ์ replay
- deallocated๋ ๋ complete๋จ (๋ฉ๋ชจ๋ฆฌ ํด์ ์ complete ์ด๋ฒคํธ ๋ฐฉ์ถ)
- ์๋ฌ๋ฅผ ๋ฐฉ์ถํ์ง ์์
- Main Thread์์ ์คํ๋จ
ControlProperty์ ์คํ์ ์ด๋ฒคํธ ์ํ์ค๊ฐ main scheduler์์ subscribe๋๋ ๊ฒ์ ๋ณด์ฅํฉ๋๋ค. (subscribeOn(ConcurrentMainScheduler.instance))
ControlEvent
UI ์์์ event๋ฅผ ๋ํ๋ด๋ Observable/ObservableType ์ ์ํ Trait์ ๋๋ค.
ControlEvent์ ํน์ง
- ์ ๋ ์คํจํ์ง ์์
- ๊ตฌ๋ ์ ์ด๊ธฐ ๊ฐ์ ๋ด๋ณด๋ด์ง ์์
- deallocated๋ ๋ complete๋จ (๋ฉ๋ชจ๋ฆฌ ํด์ ์ complete ์ด๋ฒคํธ ๋ฐฉ์ถ)
- ์๋ฌ๋ฅผ ๋ฐฉ์ถํ์ง ์์
- Main Thread์์ ์คํ๋จ
ControlEvent์ ์คํ ๋ํ ์ด๋ฒคํธ ์ํ์ค๊ฐ main scheduler์์ subscribe๋๋ ๊ฒ์ ๋ณด์ฅํฉ๋๋ค. (subscribeOn(ConcurrentMainScheduler.instance))
[์ฐธ๊ณ ์๋ฃ]
https://github.com/ReactiveX/RxSwift/blob/main/Documentation/Traits.md
'๐ iOS > RxSwift' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[RxSwift] Traits - Single, Completable, Maybe (0) | 2023.04.17 |
---|---|
[RxSwift] Relay๋? (0) | 2023.02.17 |
[RxSwift] Subject๋? (0) | 2023.02.03 |
[RxSwift] Observable (0) | 2022.05.02 |
[RxSwift] RxSwift์ ๋น๋๊ธฐ ํ๋ก๊ทธ๋๋ฐ (7) | 2022.04.07 |