RxSwift 3๊ต์ ์์ ์ด๋ฏธ ์ฌ์ฉํ ๊ฒ๋ค์ด์ง๋ง, ๊ธ์ด ๊ธธ์ด์ ธ์ 3๊ต์ ์ ๋ฆฌ๋ ๊ฐ์ด ์งํํ๋ ค๊ณ ํ๋ค. MVC, MVP, MVVM ๊ตฌ์กฐ ์์ ๊ณต๋ถํ ๋๋ ๋์์ธ ํจํด์ ๊ฐ๋
๋ง ์ผ์ถ ์ดํด๊ฐ ๋๊ณ , ์ฝ๋๋ ์ ์ดํด๊ฐ ์๊ฐ์๋๋ฐ ์ค๋ช
์ ๋ฃ๊ณ ๋ณด๋ ์ดํด๊ฐ ์ ๊ฐ์ ๋๋๋ค.
3. UI Component์ ์ฐ๋ ์ ๋ฆฌ
1. Subject
https://reactivex.io/documentation/subject.html
Data Control
- Observable์ ๋ฐํ์์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ ์์ ๋ ์ ์์ง ์๋ค.
- ์ธ๋ถ ์ปจํธ๋กค์ ์ํด ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ ์๋ subscribe ํ ์๋ ์๋ ๊ฒ์ด ํ์ํ๋ฐ, ๊ทธ๊ฒ Subject ๋ค.
- Subject์๋ 4๊ฐ์ง ์ข
๋ฅ๊ฐ ์๋๋ฐ,
- PublishSubject
- ๊ธฐ๋ณธ์ ์ธ subject
- ์ฌ๋ฌ ๊ฐ์ฒด๊ฐ subscribe ํ ์ ์์.
- PublishSubject์ ๋ฐ์ดํฐ๊ฐ ์์ฑ๋๋ฉด ๋ชจ๋ ๊ตฌ๋
์์๊ฒ ํด๋น ๋ฐ์ดํฐ๋ฅผ ๊ทธ๋๋ก ๋ณด๋ด์ค๋ค.
- BehaviorSubject
- ๊ธฐ๋ณธ๊ฐ์ ๊ฐ์ง๊ณ ์์ํ๋ค.
- ๋๊ตฐ๊ฐ subscribe ํ์๋ง์ ๊ธฐ๋ณธ ๊ฐ์ ๋ณด๋ด์ค๋ค.
- ์๋ก์ด ๊ฐ์ฒด๊ฐ subscribe ํ๋ฉด ๊ฐ์ฅ ์ต๊ทผ์ data๋ฅผ ๋ณด๋ด์ค๋ค.
- AsyncSubject
- ์ฌ๋ฌ ๊ฐ์ฒด๊ฐ ๋ฐ์ดํฐ๋ฅผ ๊ตฌ๋
ํ๋๋ผ๋ complete ๋๋ ์์ ์ ๊ฐ์ฅ ์ต๊ทผ ๋ฐ์ดํฐ๋ง ๋ณด๋ด์ค๋ค.
- ReplaySubject
- ๊ตฌ๋
์์๊ฒ ์์๋๋ก ๋ฐ์ดํฐ๋ฅผ ๋ด๋ ค์ค๋ค.
- ์๋ก์ด ๊ตฌ๋
์๊ฐ ์๊ธฐ๋ฉด ์ฌํ๊น์ง ์ ๋ฌ๋ ๋ชจ๋ ๊ฒ์ ํ๋ฒ์ ๋ด๋ ค์ค๋ค.
Hot Observable / Cold Observable
- Hot Observable : ์์ฑ๋์๋ง์ ํญ๋ชฉ์ ๋ฐฐ์ถ. ๊ตฌ๋
์๋ ํญ๋ชฉ์ด ๋ฐฐ์ถ๋๋ ์ค๊ฐ๋ถํฐ ๊ตฌ๋
ํ ์ ์๋ค.
- Cold Observable : Observer๊ฐ ๊ตฌ๋
ํ ๋ ๊น์ง ํญ๋ชฉ์ ๋ฐฐ์ถํ์ง ์๋๋ค. Observer๋ ์ด Observable ์ด emit ํ๋ ํญ๋ชฉ ์ ์ฒด๋ฅผ ๊ตฌ๋
ํ ์ ์๋ค.
2. RxCocoa
RxSwift์ ๊ธฐ๋ฅ์ UIKit์ Extension์ผ๋ก ์ถ๊ฐํ ๊ฒ. syntatic sugar๋ผ๊ณ ๋ณด๋ฉด ๋๋ค.
UI ์์
์ ํน์ง
- main thread ์์๋ง ๋์๊ฐ๋ค.
- `observeOn(MainScheduler.instance)๋ ๋ฌด์กฐ๊ฑด ๋ค์ด๊ฐ์ผ ํ๋ค.
- ์์
์ ํ๋ค๊ฐ ์๋ฌ๊ฐ ๋๋ฉด stream ์ด ๋์ด์ ธ์ ๋์ํ์ง ์๋๋ค.
.catchErrorJustReturn("")
๊ฐ์ ๊ฒ์ผ๋ก ์๋ฌ๊ฐ ๋๋ ์ฑ์ด ์ฃฝ์ง ์๊ฒ ์ฒ๋ฆฌํด์ผํ๋ค.
Observable / Driver
observeOn().catchErrorJustReturn("").bind/subscribe
๋ฅผ chaining ํ ์ฝ๋๋ฅผ
asDrive(onErrorJustReturn: "").drive(itemCountLabel.rx.text)
์ด๋ฐ ์์ผ๋ก ๋ฐ๊ฟ ์ ์๋ค.
- ์๋ฌ๊ฐ ์๊ธฐ๋ฉด ""์ผ๋ก ์ฒ๋ฆฌํ๊ณ , drive ๋ ์์์ main thread ์์ ๋์๊ฐ๋ ์ฝ๋.
Subject / Relay
- Subject ์ญ์ UI ์์
์ ํ ๋๋ ๋์ด์ง์ง ์๊ฒ ํ ํ์๊ฐ ์๋ค. -> Subject ๋์ Relay ๋ฅผ ์ฌ์ฉํ์. error ๊ฐ ๋๋ฉด ๊ทธ๋ฅ ๋ฌด์ํ๊ณ ์งํํด์ค๋ค.
- ๊ทผ๋ฐ error ๊ฐ ๋์ง ์๋๋ค๋ ์๊ธฐ๋? onError ๋ ์๊ณ onCompleted ๋ง ์๋ค๋ ๋ง๊ณผ ๊ฐ๋ค.
- relay ๋
self.menuObservable.accept($0)
์ผ๋ก ์ค๋ก์ง ์ ๋ณด๋ฅผ ๋ฐ์๋ค์ผ ์๋ง ์๋ค.
4. MVVM ์ ์ฉํ๊ธฐ
domain์์ ์ค๋ ๋ชจ๋ธ์ ๋๋ถ๋ถ ์ฐ๋ฆฌ ํ๋ฉด์์ ๋ณด์ด๋ ๋ชจ๋ธ๊ณผ ๋ค๋ฅด๋ค. -> 3๊ต์์ ํ ๊ฒ ์ฒ๋ผ ๋งตํํด์ ์จ์ผํจ.
1. MVC
- Controller(UIViewController) Input์ Controller ๊ฐ ๋ฐ๋๋ค.
- Controller ๊ฐ View ํํ
view setting, updateUI ๋ฑ์ ์ํจ๋ค.
- update์ ๊ธฐ๋ฐ์ ๋ชจ๋ธ์ด ๊ฐ์ง๊ณ ์๋ค. UIKit์ ์ข
์๋์ง ์๊ธฐ ๋๋ฌธ์ Test ํ ์ ์๋ค.
- ๋จ์
- Controller ๋ ํ
์คํธ๋ฅผ ํ ์๊ฐ ์์.. ํ
์คํธ ํ์๊ณ ๋ก์ง๋ง ๋ฐ๋ก ๋ผ์ด๋ด๊ธฐ๋ ์ ๋งค... ๊ธฐ๋ฅ์ ์ผ๋ก๋ ์ด๋ ๊ฒ ๋๋๋๊ฒ ์ข์๋๋ฐ...!
- ํด๋ผ์ด์ธํธ ๋จ ๋ด์์๋ง ๋๋ ์ ๊ฐ๋ฐํ๋ ค๋ค๋ณด๋ ์ด๋ ต๋ค.
2. MVP
- MVC๋ฅผ ๋ณด์ํ๋ฉด์, Controller ์ ์ญํ ์ ์ ํํ๋ ๊ตฌ์กฐ.
- UIViewController ๋ View ์ญํ ๋ง ํ ์ ์๋๋ก ํ๋ค. (test ํ ํ์๊ฐ ์๊ฒ. ๊ฒฐ๊ตญ ์ฌ์ฉ์ ์
๋ ฅ์ MVP์ View ๋ก ๋ฐ๋ ๊ฒ)
- ๊ด๋ จ๋ ๋ก์ง ๋ถ๋ถ๋ง ์ ๋ถ Presenter ๋ก ๋บ๋ค. ๋ก์ง์ ์ฒ๋ฆฌํ๊ณ View์ ๊ทธ๋ฆฌ๋ผ์ฝ ์ํจ๋ค.
- View ํ๋๋ฅผ Presenter ํ๋๊ฐ ๋ด๋นํ๋ค (1:1 ๊ตฌ์กฐ)
- Presentor ๋ test ํ ์ ์๋ค.
- ๋จ์
- View ๋ฅผ ์
๋ฐ์ดํธ ํ ๋๋ง๋ค presenter ์ ๋ค ๋ฌผ์ด๋ด์ผ ํ๊ณ , Preseter ๋ ์ ๋ถ ๊ทธ๋ ค์ View ํํ
๋ณด์ฌ์ฃผ๋ผ๊ณ ์์ผ์ผ ํ๋ค. -> ๋ฒ๊ฑฐ๋ก์..
- 1:1 ๊ด๊ณ๋ผ ๋น์ทํ View๋ฅผ ๋ณด์ฌ์ฃผ๋๋ฐ๋ VP ์์ด ์ฌ๋ฌ๊ฐ๊ฐ ๋๋ค.
3. MVVM
- MVP ํจํด์ ๋จ์ ์ ๊ทน๋ณตํ๊ธฐ ์ํด presenter ๋ฅผ ๊ณตํต์ผ๋ก (ViewModel) ์ฌ์ฉ.
- viewModel ์ View ์ ๋ณด์ฌ์ค ๋ฐ์ดํฐ๋ง ๊ฐ์ง๊ณ ์๊ณ , View ํํ
์ง์ ์ ์ผ๋ก ๋ญ ๊ทธ๋ฆฌ๋ผ๊ณ ์ง์ํ์ง ์๋๋ค.
- viewModel : view = 1 : n -> ๋น์ทํ ๊ตฌ์กฐ์์ ๊ฐ์ viewModel ์ ๊ฐ์ง๊ณ , ํ๋ฉด์๋ง ๋ค๋ฅด๊ฒ ๋ํ๋ผ ์ ์๋ค.
- Data Binding : viewModel ์ ๋ฐ๋ผ๋ณด๋ค๊ฐ ๊ฐ์ด ๋ฐ๋๋ฉด ์ฝ์ด์์ view ์ ๋ํ๋ธ๋ค. ๊ผญ ์ ์ฌํ์ง ์๋๋ค. ์๋ฅผ ๋ค์ด์ SwiftUI ์์๋
needUpdateUI
๋ฅผ ๋ง๋ค๊ณ , ํ๋ฉด์ ํํ๋ ๋ณ์์ didSet { needUpdateUI() }
viewDidLoad ์์ viewModel.needUpdateUI = { ์
๋ฐ์ดํธ ํด์ผ ํ๋ viewModel ์์ ๋ฃ๊ธฐ }
์ ๋ฌํด์ค์ ํ๋ฉด์ ๋ณด์ด๋ ์์๋ฅผ ๋ฐ๊พธ๊ธฐ๋ ํ๋ค.
- ํ๋์ view์ ๋ํ viewModel ์ ๋ฐ๋ก ๋ ์๋ ์๋ค.
var cellViewModels: [CellViewModel] = []
viewModel.menuObservable
.bind(to: tableView.rx.items(cellIdentifier: cellId, cellType: MenuItemTableViewCell.self)) { index, item, cell in
let cellViewModel = self.viewModel.cellViewModels[index]
cell.setViewModel(cellViewModel)
cell.onChange = { [weak self] increase in
self?.viewModel.changeCount(item: item, increase: increase)
}
}
.disposed(by: disposeBag)
var disposeBag = DisposeBag()
func setViewModel(viewModel: CellViewModel) {
viewModel.title.bind(title.rx.text)
}
override func prepareForReuse() {
disposeBag = DisposeBag()
}
์ถ์ฒ
์ ํ๋ธ ๊ณฐํ๊น๋ RxSwift 4์๊ฐ์ ๋๋ด๊ธฐ
https://youtu.be/iHKBNYMWd5I
RxSwift 4์๊ฐ์ ๋๋ด๊ธฐ github
https://github.com/iamchiwon/RxSwift_In_4_Hours
์ฑ๊ธฐํ๊ฑธ ๊ณต๋ถํ์๋๊ตฐ์!