Bibi's DevLog ๐ค๐
[๊ณฐํ๊น๋] RxSwift + MVVM ์ ๋ฆฌ ๋ณธ๋ฌธ
[๊ณฐํ๊น๋] RxSwift + MVVM ์ ๋ฆฌ
https://www.youtube.com/watch?v=iHKBNYMWd5I&list=PL03rJBlpwTaBrhux_C8RmtWDI_kZSLvdQ
1๊ต์
JSON ๋ค์ด๋ก๋ ์์ ์ด ๋๊ธฐ์ ์ผ๋ก ๊ตฌํ์ด ๋์ด ์๋ค.
LOAD ํด๋ฆญ ์ ํ์ด๋จธ๋ฅผ ํฌํจํด ํ๋ฉด ์ ์ฒด๊ฐ ๋ฉ์ถ๊ณ , ๋ค์ด๋ก๋๊ฐ ๋๋๋ฉด ๋ค์ ํ๋ฉด์ด ์์ง์ธ๋ค.
๋น๋๊ธฐ๋ก ๋ง๋ค์ด๋ณด์.
๋น๋๊ธฐ๋?
: ํ์ฌ ์ค๋ ๋์์ ์์ ์ ์งํํ๋ฉด์, ๋ค๋ฅธ ์ค๋ ๋๋ฅผ ์ด์ฉํด ๋ค๋ฅธ ์์ ์ ๋์์ ์งํํ๋ ๊ฒ.
๋ค๋ฅธ ์ค๋ ๋์์ ๋น๋๊ธฐ๋ก ์ฒ๋ฆฌํ ์์ ์ด ์๋ฃ๋๋ฉด, ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ์์์ผ ํ๋ค.
DispatchQueue.global().async { ... }
- ์ด๋ ๊ฒ ์ฒ๋ฆฌํ๋ฉด ํ๋ก๊ทธ๋จ์ด ์ฃฝ๋๋ค.
- UI๋ฅผ ๊ฑด๋๋ฆฌ๋ ๋ถ๋ถ์ global()์ค๋ ๋๊ฐ ์๋ ๋ฉ์ธ ์ค๋ ๋๋ก ํด์ผ ํ๊ธฐ ๋๋ฌธ.
- UI๊ด๋ จ ์ฝ๋๋ฅผ ๋ค์ ํ ๋ฒ
DispatchQueue.main.async { ... }
completion handler
์์ swift๋ก ๋น๋๊ธฐ ๊ฒฐ๊ณผ๊ฐ์ ๋ฐ์์ค๋ ค๋ฉด completion @escapimg
ํธ๋ค๋ฌ๋ฅผ ์จ์ผ ํ๋ค - ํด๋ก์ ๋ฐฉ์
@escaping
ํด๋ก์ ๋ฅผ ํตํด, ๋ค๋ฅธ ์ค๋ ๋์์ ์ฒ๋ฆฌ๋ ๋น๋๊ธฐ ์์ ๊ฒฐ๊ณผ๋ฅผ ๋๊ฒจ์ค๋ค- ๋จ์ : ๋น๋๊ธฐ๋ฅผ ์ฌ๋ฌ ๋ฒ ๊ฒน์ณ ์จ์ผ ํ๋ ์ํฉ ๋ฐ์ ์ ์์๋ณด๊ธฐ ๋ณต์กํด์ง๋ค(depth๊ฐ ๊น์ด์ง๋ค)
๊ทธ๋์ ๋น๋๊ธฐ ๊ฒฐ๊ณผ๊ฐ์ ํด๋ก์ ๊ฐ ์๋๋ผ, ๋ฆฌํด๊ฐ์ผ๋ก ํํํ๋ ค๋ ์๋๊ฐ ์๊ฒผ๋ค.
๋์ค์์๊ธฐ๋๋ฐ์ดํฐ
- ๋น๋๊ธฐ์์
๊ฒฐ๊ณผ๊ฐ์ ๋ฆฌํด๊ฐ์ผ๋ก
๋์ค์์๊ธฐ๋๋ฐ์ดํฐ<์ ๋ค๋ฆญ>
์ผ๋ก ๋ฐํ ๋์ค์์๊ธฐ๋๋ฐ์ดํฐ<์ ๋ค๋ฆญ>.๋์ค์์๊ธฐ๋ฉด{...}
์์ ๋์ค์ ์๊ธฐ๋ฉด ํ ์์ ์ ์ฒ๋ฆฌํ๋ค.-
class ๋์ค์์๊ธฐ๋๋ฐ์ดํฐ<T> { private let task: (@escaping (T) -> Void) -> Void init(task: @escaping (@escapint (T) -> Void) -> Void) { self.task = task } // ๋ฐ์ดํฐ ์์ฑ์ ํด๋ก์ ๋ฅผ ๋ฐ์ ๊ฐ์ง๊ณ ์์ func ๋์ค์์ค๋ฉด(_ f: @escaping (T) -> Void) { task(f) } // ๋์ค์์ค๋ฉด ๋ฉ์๋ ํธ์ถ ์, ๊ฐ์ง๊ณ ์๋ ํด๋ก์ ๋ฅผ ์คํ }
- ์ฌ์ฉํ ๋๋ ์ด๋ ๊ฒ ์ฌ์ฉ
-
// ๋์ค์์๊ธฐ๋๋ฐ์ดํฐ ๋ง๋ค๊ธฐ func downloadJson(_ url: String) -> ๋์ค์์๊ธฐ๋๋ฐ์ดํฐ<String?> { return ๋์ค์์๊ธฐ๋๋ฐ์ดํฐ() { f in DispatchQueue.global().async { let url = URL(string: url)! let data = try! Data(contentsOf: url) let json = String(data: data, encoding: .utf8) DispatchQueue.main.async { f(json) } } } } // ๋์ค์์๊ธฐ๋๋ฐ์ดํฐ ๋ฅผ ์ฌ์ฉํ ๋ : .๋์ค์์ค๋ฉด ์ผ๋ก ์ฌ์ฉ func onLoad() { let json: ๋์ค์์๊ธฐ๋๋ฐ์ดํฐ<String?> = downloadJson(URL) json.๋์ค์์ค๋ฉด { json in self.editView.text = json // ... } }
-
RxSwift
- ์์ ๊ฐ์
๋์ค์์๊ธฐ๋๋ฐ์ดํฐ
๋ก ๋น๋๊ธฐ์์ ๊ฒฐ๊ณผ๊ฐ์ ์ ๋ฌํ๋ ์ ํธ๋ฆฌํฐ ์ค์ ํ๋๊ฐ RxSwift์ด๋ค.๋์ค์์๊ธฐ๋๋ฐ์ดํฐ<ํ์ >
=Observable<ํ์ >
.๋์ค์์๊ธฐ๋ฉด
=.subscribe
- ์ฆ RxSwift์ ์ฉ๋๋ "๋น๋๊ธฐ๊ฒฐ๊ณผ๊ฐ์ ๋ฆฌํด๊ฐ์ผ๋ก ์ฃผ๊ธฐ ์ํด ๋ง๋ ์ ํธ๋ฆฌํฐ"์ด๋ค.
- Disposable
.dispose()
: ๋ฒ๋ฆฐ๋ค๋ ๋ป. ๋์์ ์ทจ์์ํด.- ์ด ๋ฉ์๋๋ฅผ ํธ์ถํ๋ฉด Observable ์์ ์ด ๋๋๋ ๋๋์ง ์์๋ ์ฆ์ ์์ ์ ์ทจ์์ํจ๋ค.
- ์ํ์ฐธ์กฐ
- ์ํ์ฐธ์กฐ๋ ์ ์๊ธฐ๋๊ฐ?
- ํด๋ก์ ๊ฐ ์์ ๋ด๋ถ์ ์ ์ธ๋
self
๋ฅผ ์บก์ฒํ๋ฉด์ ๋ ํผ๋ฐ์ค ์นด์ดํธ๊ฐ ์ฆ๊ฐํ๊ธฐ ๋๋ฌธ์ ์ํ์ฐธ์กฐ๊ฐ ์๊ธด๋ค. - ํด๊ฒฐ1 : ํด๋ก์ ์ ํ๋ผ๋ฏธํฐ๋ฅผ
[weak self]
๋ก ๋ง๋ค๊ณ ,self
๋ฅผself?
๋ก ์ ์ธํด ํด๊ฒฐ ๊ฐ๋ฅ - ํด๊ฒฐ2 : ํด๋ก์ ๋ฅผ ์ ๋ ์์ ๊ธฐ
- Rx์ ๊ฒฝ์ฐ
.completed
/.error
/.disposed
๋ฅผ ํธ์ถํ๋ฉด ํด๋ก์ ๊ฐ ์ฌ๋ผ์ง๋ค.
- Rx์ ๊ฒฝ์ฐ
- ํด๋ก์ ๊ฐ ์์ ๋ด๋ถ์ ์ ์ธ๋
- ์ํ์ฐธ์กฐ๋ ์ ์๊ธฐ๋๊ฐ?
RxSwift ์ฌ์ฉ๋ฐฉ๋ฒ
- ๋น๋๊ธฐ๋ก ์๊ธฐ๋ ๋ฐ์ดํฐ๋ฅผ Observable๋ก ๊ฐ์ธ์ ๋ฆฌํดํ๋ ๋ฐฉ๋ฒ = (
return Observable<T>.create {...}
)
return Observable.create() { emitter in
let url = URL(string: url)!
let task = URLSession.shared.dataTask(with: url) { (data, _, err) in
guard err == nil else {
emitter.onError(err!)
return
}
if let dat = data, let json = String(data: dat, encoding: .utf8) {
emitter.onNext(json)
}
emitter.onConpleted()
}
task.resume()
return Disposables.create() {
task.cancel()
}
}
- Observable๋ก ์ค๋ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ ์ฒ๋ฆฌํ๋ ๋ฐฉ๋ฒ = (
Observable.subscribe { ... }
)- Observable์ ๋ฆฌํด๊ฐ์ Disposable
observable.subscribe { event in
switch event {
case .next(let data):
break
case .error(let err):
break
case .completed:
break
}
}
- Observable์ ์ข
๋ฃ : .completed, .error, .disposed
- ์ข ๋ฃ ์์ ์ ํด๋ก์ ๊ฐ ์ฌ๋ผ์ง๋ฏ๋ก ์ํ์ฐธ์กฐ๋ฅผ ์๋ฐฉํ ์ ์๋ค.
Observable์ ์๋ช ์ฃผ๊ธฐ
- create
- subscribe (๋์ ์์)
- onNext (๋ฐ์ดํฐ ์ ๋ฌ)
- onCompleted / onError (์คํ ์ข ๋ฃ)
- disposed (๋์์ด ๋๋ Observable์ ์ฌ์ฌ์ฉํ ์ ์์ - ์ผํ์ฉ์. ๋ค์ subscribeํด ๋์์ ์์ํด์ผ ํจ)
Observable์ ์ ์ธ = ์คํ ์ด ์๋๋ค.
.subscribe
๋ฅผ ํ ๋ ์คํ์ด ๋๋ค!
SugarApi - ์ฐ์ฐ์
RxSwift์ ๊ธฐ๋ณธ๊ธฐ๋ฅ์ ์ถ์ฝํด ์ฃผ๋ API. ๊ท์ฐฎ์ ์์ ๋ค์ ์๋ตํด ์ค๋ค
์ฐ์ฐ์ : ๋ฐ์ดํฐ๋ฅผ ์ค๊ฐ์ ๋ฐ๊พธ๋ API (map, observeOn..)
์ฐ์ฐ์์ ์ข ๋ฅ
reactivex.io
ํ์ด์ง์ ๋ค์ด๊ฐ operator๋ก ๊ฒ์ํ๋ฉด ์ฐ์ฐ์ ์ข
๋ฅ๊ฐ ๋์จ๋ค.
- https://reactivex.io/documentation/operators.html
- ๊ตต์ ๊ธ์จ๋ก ๋ ์ฐ์ฐ์๋ : ๋ํ ๊ธฐ๋ฅ์ ํ๋ ์ฐ์ฐ์
- ์ผ๋ฐ ๊ธ์จ๋ก ๋ ์ฐ์ฐ์ : ๋ํ ์ฐ์ฐ์์ ํ์ ์ฐ์ฐ์
- ๋ด๊ฐ ์ํ๋ ์ฐ์ฐ์๋ฅผ ๋นจ๋ฆฌ ์ฐพ๋ ๋ฐฉ๋ฒ?
- ์ฑํ ๋ฐฉ์ ๋ฌผ์ด๋ณด๊ธฐ(best)ใ ใ
- ๊ณต์๋ฌธ์์์ ์ฐพ์๋ณด๊ธฐ
์์ฃผ ์ฐ์ด๋ ์ฐ์ฐ์
Observable.just("๋ฐ์ดํฐ")
: ๋ฐ์ดํฐ ํ๋ ์ ๋ฌํ ๋ ์ฌ์ฉํ๋ ์ฐ์ฐ์Observable.from(["hello", "world"])
: ์ฌ๋ฌ ๋ฐ์ดํฐ๋ฅผ ํ๋์ฉ ์ ๋ฌํ ๋ ์ฌ์ฉํ๋ ์ฐ์ฐ์.subscribe(onNext: { ... } )
: next, completed, error, dispose ์ค ํ์ํ ๊ฒ๋ง ํ๋ผ๋ฏธํฐ๋ก ์ฌ์ฉ ํ๋ค..observeOn()
: ๋ค์ ์ค๋ถํฐ ์คํํ ์ค๋ ๋๋ฅผ ์ง์ - downstream(์๋๋ก) ์ํฅ์ ์ค๋ค
- DispatchQueue.main.async { ... }๋ฅผ ์ฌ์ฉํ์ง ์๊ณ ํ ์ค๋ก ํด๊ฒฐ
- ๋งค๊ฐ๋ณ์๋ก๋
Scheduler
ํ์ ์ ๋ฃ๋๋ค. - ์๋ฅผ ๋ค์ด
MainScheduler.instance
,ConcurrentDispatchQueueScheduler(qos: .defalut)
..
.subscribeOn()
: ๋งจ ์ฒ์ ์์ํ ์ค๋ ๋๋ฅผ ์ง์ - ์ด๋์์ ํธ์ถํ๋ ๋งจ ์ฒ์ ์ค๋ ๋๋ฅผ ์ง์ ํจ
- upstream(์๋ก) ์ํฅ์ ์ค๋ค
.map { ... }
,.filter { ... }
....buffer
: ์ฌ๋ฌ ๋ฐ์ดํฐ๊ฐ ์์ ๋ ์ง์ ๋ ๊ฐฏ์๋งํผ ๋ฌถ์ด์ ๋ด๋ ค๋ณด๋ด์ค.scan
: ์ง์ ๋ฐ์ดํฐ์ ์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ง๊ณ ์ฐ์ฐ์ ์ํ.take(ํ์)
: ์ง์ ํ์๋งํผ๋ง ์ํObservable.debug()
: Observable๋ด์์ ์ด๋ค ๋ฐ์ดํฐ๊ฐ ์ ๋ฌ๋๋์ง๋ฅผ ๋ณด์ฌ์ค
๋ง๋ธ ๋ค์ด์ด๊ทธ๋จ marble diagram
๋ง๋ธ ๋ค์ด์ด๊ทธ๋จ๋ง ์ดํดํ๋ฉด ์ฐ์ฐ์์ ๋์์ ์ ์ ์๋ค.
- ๋๊ทธ๋ผ๋ฏธ = ๋ฐ์ดํฐ ๋ฅผ ์๋ฏธํจ
- ๋ค๋ชจ = ์ฐ์ฐ์ ๋ฅผ ์๋ฏธํจ
- ์๊น : ์ฐ๋ ๋๋ฅผ ์๋ฏธํจ
- ํ์ดํ = Observable ์ ์๋ฏธํจ
- ์ธ๋ก์ค = complete ๋ฅผ ์๋ฏธํจ
2๊ต์
Stream์ ๋ถ๋ฆฌ ๋ฐ ๋ณํฉ
- ๋ณํฉ
.merge
: ์ฌ๋ฌ Observable์ ๋ฌถ์ด ํ๋์ Observable๋ก ๋จ์ํ๊ฒ ํฉ์น ๊ฒ- ๋ Observable์ ๋ฐ์ดํฐํ์ ์ด ๊ฐ์์ผ๋ง ํ๋ค
.zip
: ๋ Observable์ ๋ฐ์ดํฐ๋ฅผ ํฉ์ณ ์์ผ๋ก ๋ง๋ค์ด ๋ณด๋- ๋ Observable์ ๊ธธ์ด๊ฐ ๊ฐ์ผ๋ฉด ์์ผ๋ก ๋ง๊ฒ ๋ณด๋
- ๊ธธ์ด๊ฐ ๋ค๋ฅด๋ฉด ์งง์ ์ชฝ์ ๊ธธ์ด์ ๋ง์ถค
.combineLatest
: .zip๊ณผ ๋ฌ๋ฆฌ ๋ฐ์ดํฐ ์ ๊ฐฏ์๊ฐ ์๋ง์๋, ์ด์ ๋ฐ์ดํฐ ์ค ์ต๊ทผ ๊ฒ์ ๊ฐ์ง๊ณ ์์ ๋ง๋ค์ด ์ค๋ค
Disposable
- ์ฐ์ฐ์ ์ทจ์ํ๊ณ ์ถ์ ๋, ๊ทธ๋ฆฌ๊ณ ํด๋น ์คํธ๋ฆผ์ ์ญ์ ํ ๋ ์ฌ์ฉ
- disposeBag : Disposable๋ค์ ๋ด๋ ๊ฐ๋ฐฉ. ์ฌ๊ธฐ์ ๋ด์๋๋ฉด ํด๋์ค๊ฐ ์ง์์ง ๋ ์๋์ผ๋ก ์คํธ๋ฆผ๋ค์ ์ง์์ค๋ค.
step2 ์์ (RxSwift + MVVM)
- RxSwift๋ฅผ ์ฌ์ฉํด ์ด๋ป๊ฒ json ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ -> ํ ์ด๋ธ๋ทฐ์ ๋ฟ๋ฆฌ๋์ง๋ฅผ ๋ณด์ฌ์ฃผ๋ ์์ ์ด๋ค.
- ์ฐธ๊ณ ์๋ฃ๋ก ๋ณด๊ธฐ
step3 ์์
- step3 empty๋ฅผ ์ด์ด์ ๋ทฐ์ ์ฐ๊ฒฐ, RxSwift ์ ์ฉ, RxSwift + MVVM ์ ์ฉ ํด๋ณด๊ธฐ ์ฐ์ต์ ํ๋ค.
MVVM
๋ทฐ๋ชจ๋ธ = ๋ทฐ๋ฅผ ์ํ ๋ชจ๋ธ,
- ๋ทฐ์ ํ์ํ ๋ชจ๋ธ์ด๋ผ๋ ๋ป์ด๋ค!
- ๋ทฐ์ ํ์ํ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ง๊ณ ์๋ค.
- ์ฑ์ ํ์ํ ๋ก์ง์ ๋ทฐ๋ชจ๋ธ์ด ๊ฐ๋๋ค.
๋ทฐ์ปจํธ๋กค๋ฌ
- ๋ทฐ ์์๊ฐ ์ด๋ป๊ฒ ๋ณด์ฌ์ง์ง์ ๋ํ ๋์๋ง ์ง์ ํ๋ค.
- ๋ก์ง์ ์ฒ๋ฆฌํ์ง๋ ์๋๋ค.
- ๋ทฐ๋ชจ๋ธ์ ๊ฐ๋๋ค (๋ทฐ๋ชจ๋ธ์ ๊ทธ ๋ก์ง์ด ์๋ค)
RxSwift ์ ์ฉ ์ง์
- ๋ฐ์ดํฐ๊ฐ ๋ฐ๋๋ฉด ๋ด๊ฐ ๋ทฐ ์ ๋ฐ์ดํธ ๋ฉ์๋๋ฅผ ์คํ์ํค๋ ๊ฒ ์๋๋ผ, ๋ทฐ๊ฐ ์์์ ์ ๋ฐ์ดํธ๋์์ผ๋ฉด ์ข๊ฒ ๋ค.
Subject
: Observable์ฒ๋ผ subscribeํด ๊ฐ์ ๋ฐ์์ฌ ์ ์๊ณ , (์ถ๊ฐ์ ์ผ๋ก) ์ธ๋ถ์์ ๊ฐ์ ์ปจํธ๋กคํ ์๋ ์๋ ๊ฐ์ฒด.
- ์ ์ฐ๋๊ฐ?
- create ์์ ๋ฟ ์๋๋ผ, ์ด๋ฏธ ๋ง๋ค์ด์ง subject์ ๋ํด์๋ ๊ฐ ๋ณ๊ฒฝ ๊ฐ๋ฅ
- Observable๊ณผ์ ์ฐจ์ด์ : ์์ฑ๋ ์ดํ์๋ ์ธ๋ถ์์ ๊ฐ์ ๋ฐ๊ฟ ์ ์๋ค. (๋ฐ์ดํฐ ์ฃผ์ ๊ฐ๋ฅ)
- PublishSubject์ BehaviorSubject๋ฅผ ๊ฐ์ฅ ๋ง์ด ์ฌ์ฉํ๋ค.
Subject์ ์ข ๋ฅ
PublishSubject()
- ์์ ์ subscribeํ ๊ฐ์ฒด์๊ฒ ๋ฐ์ดํฐ๋ฅผ ๋ด๋ ค์ค
- ์ดํ subscribeํ ๊ฐ์ฒด์๊ฒ๋ ๊ทธ ์ดํ์ ๋ฐ์ดํฐ๋ฅผ ๋ด๋ ค์ค
BehaviorSubject(๊ธฐ๋ณธ๊ฐ)
- subscribeํ๋ฉด ๊ธฐ๋ณธ๊ฐ์ ๋ด๋ ค์ค
- ์ดํ subscribeํ ๊ฐ์ฒด์๊ฒ๋ ๊ฐ์ฅ ์ต๊ทผ์ ๋ฐ์ดํฐ + ๊ทธ ์ดํ์ ๋ฐ์ดํฐ๋ฅผ ๋ด๋ ค์ค
AsyncSubject
- subscribeํ ๊ฐ์ฒด๋ค์ด ์์ด๋ ๋ฐ์ดํฐ๋ฅผ ๋ด๋ ค์ฃผ์ง ์๋ค๊ฐ,
- ์์ ์ด complete๋๋ ์์ ์ ๋งจ ๋ง์ง๋ง ๋ฐ์ดํฐ๋ง subscribeํ ๊ฐ์ฒด๋ค์๊ฒ ๋ด๋ ค์ฃผ๊ณ ๋๋จ
ReplaySubject
- ์๋กญ๊ฒ subscribeํ ๊ฐ์ฒด์๊ฒ ๊ทธ๋์ ๋ฐ์ํ ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ๋ด๋ ค์ค
RxCocoa
: RxSwift์ ์์๋ค์ UIKit์ ์ ์ฉ์ํจ ๊ฒ!
UIKit์์.rx
๋ก ์ฌ์ฉ ๊ฐ๋ฅ
.bind(to: ๋ฐ์ธ๋)
:subscribe(onNext: { ... } )
๋์ ์ฌ์ฉ[weak self]
๋ฅผ ์ฌ์ฉํ์ง ์์๋ ์์์ ์ํ์ฐธ์กฐ๋ฅผ ์๋ฐฉํด ์ค๋ค.- menuObservable.bind(to: totalPrice.rx.text) : dequeue์ญํ ์ ๋์ ํจ
- index = indexPath
- item = menu ํ๋ (menuObservable ๋ฐฐ์ด์ ๊ฐ์ฒด ํ๋)
- cell = reusableCellํ๋
- ์์ : bind(to: tableView.rx.items())
UI์์ ์ ํน์ง - Driver, Relay
- UI์ฐ๋ ๋์์๋ง ์ฒ๋ฆฌํด์ผ ํจ = ๋ฉ์ธ ์ฐ๋ ๋
- ํญ์
.observeOn(MainScheduler.instance)
๋ฅผ ๋ฃ์ด์ค์ผ ํจ
- ํญ์
- ์คํธ๋ฆผ์ด ๋์ด์ง๋ฉด ์ ๋จ - ์๋ฌ๊ฐ ๋๋ค๊ณ ํด์ ํ๋ฉด์ด ๋จนํต์ด ๋๋ฉด ์ ๋จ. ์๋ฌ๊ฐ ๋ฐ์ํด๋ ๊ทธ๊ฑธ ์๋ ค์ฃผ๊ณ ๋ค์ ์ ์์ํ๋ก ๋์๊ฐ์ผ
- ํญ์
.catchErrorJustReturn()
์ฒ๋ฆฌ๊ฐ ํ์ํจ
- ํญ์
- ์์ ๋ ์ฝ๋๊ฐ ํญ์ ๋ค์ด๊ฐ์ผ ํ๋ ๊ฒ ๊ท์ฐฎ์ผ๋๊น, ์ถ์ฝํ๋ ์ API ๋ฑ์ฅ - Driver
- Driver : UI์์
์ ์ํ ๋์ด์ง์ง ์๋ Observable.
asDriver(onErrorJustReturn:์๋ฌ๋ฐ์์์ฒ๋ฆฌํ ๊ธฐ๋ณธ๊ฐ)
.drive(UI์ค๋ ๋์์ ํ ๋์)
- ํญ์ UI์ค๋ ๋์์ ๋์๊ฐ.drive(itemCountLabel.rx.text)
- Driver์ ๋ํด์๋ drive()๋ง ํ๋ฉด ๋๋ค (subscribe ํ์์์)
- Relay : UI์์
์ ์ํด ๋์ด์ง์ง ์๋ subject. (RxRelay)
- subject์ ๋ค ๋๊ฐ์๋ฐ ๋์ด์ง์ง ์์. ์ค๋ก์ง .next๋ง ์กด์ฌํจ
.onNext()
๋์.accept()
๋ง ์ฌ์ฉํจ
MVC, MVP, MVVM ์ ๋ฆฌ
- ๊ธฐํ์๊ฐ ๋์์ ๋, ์๋ฒ๊ฐ ์์ฑ๋์ง ์์์ด๋ iOS๋ iOS๋๋ก ๊ฐ๋ฐ์ ํ๊ณ ์์ด์ผ ํ๋ค.
- ์ฐ๋ฆฌ๊ฐ ๋จผ์ ๊ฐ๋ฐํ ๋ชจ๋ธ(๋ทฐ ๋ชจ๋ธ)๊ณผ, ์๋ฒ๊ฐ ๋ณด๋ด์ค ๋ชจ๋ธ (๋๋ฉ์ธ ๋ชจ๋ธ)์ ๋ณดํต ๋ค๋ฅด๋ค.
- ๋๋ฉ์ธ ๋ชจ๋ธ -> ๋ทฐ ๋ชจ๋ธ Converting ์์ ์ด ํ์ํ๋ค.
MVC
- Model
- UIKit์ ๋ ๋ฆฝ์ (ํ ์คํธ ๊ฐ๋ฅ)
- View :
- UIKit์ ์์กด์
- Controller
- User Input์ ๋ฐ๋๋ค (IBAction)
- View Setting์ ํ๋ค
- ๋จ์ : ์ปจํธ๋กค๋ฌ ์ญํ ์ด ๋๋ฌด ํผ & ํ ์คํธํ ์ ์๋ ๊ฒ ๋ชจ๋ธ๋ฟ์
MVP
View์ Presenter๊ฐ 1:1๊ด๊ณ
- Model
- View (UIViewController)
- ๋ทฐ + ๋ทฐ์ปจ
- ์ฌ์ฉ์ input์ ๋ฐ์
- ์ด๋ค ์ฒ๋ฆฌ๋ฅผ ํ ์ง๋ presenter์๊ฒ ๋ฌผ์ด๋ด - ํ๋จ์ ํ์ง ์์
- Presenter
- ๋ทฐ์ปจ์ ๋ก์ง์ presenter๋ก ๋ถ๋ฆฌ
- ๋ทฐ์ ๊ทธ๋ ค์ง ์์๋ฅผ ์ง์ ํจ
- Model๊ณผ Presenter๋ผ๋ ๋ ๋ถ๋ถ์ ํ ์คํธํ ์ ์๋ค.
- ๋จ์ : 1:1๊ด๊ณ์ด๊ธฐ ๋๋ฌธ์ Presenter๊ฐ ๋๋ฌด ๋ง์์ง, Presenter๊ฐ ๋ชจ๋ ๊ฑธ ํ๋จํด์ผ ํจ
MVVM
- Model
- View (UIViewController)
- ๋ทฐ + ๋ทฐ์ปจ
- ๋ทฐ๊ฐ ๋ทฐ๋ชจ๋ธ์ ๋ฐ๋ผ๋ณด๊ณ ์๋ค๊ฐ, ๋ทฐ๋ชจ๋ธ์ ๋ฐ๋๋ฉด ์ค์ค๋ก ๊ทธ๋ฆฌ๋๋ก ๋ฐ์ธ๋ฉํด๋
- ViewModel
- Presenter์์ ์ฐจ์ด์ : ViewModel์ด View์๊ฒ ์ง์ํ์ง ์์ (ํ์ดํ๊ฐ ๋จ๋ฐฉํฅ์)
- ๋ทฐ์ ๋ณด์ฌ์ง๋ ๋ฐ์ดํฐ๋ง ๊ฐ์ง๊ณ ์์
- Model๊ณผ ViewModel ํ ์คํธ ๊ฐ๋ฅ
Q. ๋ฐ์ดํฐ๋ฐ์ธ๋ฉ ํ์์ธ๊ฐ์?
- ํ์๋ ์๋๋ค ๊ทผ๋ฐ ํ๋ฉด ํธํ๋ค
- ๋ฐ์ดํฐ๋ฐ์ธ๋ฉ ๋์ swiftUI๋ฐฉ์์ธ
didSet()
๋ฑ์ ํ์ฉํ ์๋ ์๋ค - ๊ทผ๋ฐ ์ฝ๋๊ฐ ๊ธธ์ด์ง๋ ๊ทธ๋ฅ .bind๋ฅผ ์ฐ๋ ๊ฒ!