๐Ÿ“ RxSwift | MVVM with RxSwift

Thanks to @fimuxd ๐Ÿ–ค

์ฐธ๊ณ ์ž๋ฃŒ
https://github.com/ReactiveX/RxSwift
https://github.com/fimuxd/RxSwift
iOSใ‚ขใƒ—ใƒช่จญ่จˆใƒ‘ใ‚ฟใƒผใƒณๅ…ฅ้–€ – https://peaks.cc/books/iOS_architecture


MVVM

๊ตฌ์กฐ

View (binding) ViewController (input/output) ViewModel (update/notify) Model



1. Model
– UI์™€ ์ƒ๊ด€์—†๋Š” ์ˆœ์ˆ˜ํ•œ ๋กœ์ง๊ณผ ๋ฐ์ดํ„ฐ.
– View, ViewModel ์— ์˜์กดํ•˜์ง€ ์•Š์Œ (= Model๋งŒ์œผ๋กœ๋„ ๋นŒ๋“œ ๊ฐ€๋Šฅ)

2. View (ViewController)
– ์œ ์ €์˜ ์กฐ์ž‘์„ ๋ฐ›๊ณ , ํ™”๋ฉด ํ‘œ์‹œ๋ฅผ ๋‹ด๋‹น.
View๋Š” ViewModel๊ฐ€ ๊ฐ–๊ณ ์žˆ๋Š” ์ƒํƒœ์™€ ๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ์„ ํ•œ๋‹ค.
– ViewModel์ด ๊ฐ–๊ณ ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€๊ณต, ์—…๋ฐ์ดํŠธํ•ด์„œ ๋ฐ”์ธ๋”ฉํ•œ View๊นŒ์ง€ ์—…๋ฐ์ดํŠธ

– ViewController
– View์˜ ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ  UI ๊ตฌ์„ฑ์š”์†Œ๋ฅผ ๋ฐ”์ธ๋”ฉ ํ•  ๋•Œ๋งŒ, ViewModel, View์™€ ํ†ต์‹ ํ•œ๋‹ค.

3. ViewModel
– View์™€ Model ์‚ฌ์ด์˜ ํ™”๋ฉด ํ‘œ์‹œ๋ฅผ ์œ„ํ•œ ๋งค๊ฐœ์ฒด.
– View์— ํ‘œ์‹œํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ–๋Š”๋‹ค.
– View์—์„œ ์ด๋ฒคํŠธ๋ฅผ ๋ฐ›๊ณ  Model์˜ ์ฒ˜๋ฆฌ๋ฅผ ์‹คํ–‰.
– View์—์„œ ์ด๋ฒคํŠธ๋ฅผ ๋ฐ›์•„ ์ฒ˜๋ฆฌํ•˜๊ณ  ๊ฐ’์„ ์—…๋ฐ์ดํŠธ.
ํ™”๋ฉดํ‘œ์‹œ๋ฅผ ์œ„ํ•œ ๋กœ์ง๋ฅผ ํ•จ๊ป˜ ๋‹ด๋‹นํ•˜๋Š” ๊ฒƒ์ด ViewModel
– MVP์˜ Presenter์™€ ๋น„์Šทํ•˜์ง€๋งŒ, Presenter๋Š” View์˜ ์ฐธ์กฐ๋ฅผ ์œ ์ง€ํ•˜์ง€๋งŒ MVVM์˜ ViewModel์€ ๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ์— ์˜ํ•ด ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ธฐ ๋•Œ๋ฌธ์— View์˜ ์ฐธ์กฐ๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š๋‹ค.




์‹ค์ œ ์˜ˆ์ œ ์—ฐ์Šต

1. iOS ์„ค๊ณ„ ํŒจํ„ด ์˜ˆ์ œ
๐Ÿ”—

2. https://github.com/fimuxd/RxSwift, Lecture 23 ์˜ˆ์ œ๋ฅผ ์กฐ๊ธˆ ๋ณ€๊ฒฝ
Twitter ๋Œ€์‹  Starwars ์ธ๋ฌผ API๋กœ ๋ณ€๊ฒฝ.
UITableView, MVVM, CustomCell ๊ตฌํ˜„์€ ๋™์ผ.
๐Ÿ”—

1) ํ•ด๊ฒฐํ–ˆ์ง€๋งŒ ๊ณ ๋ฏผ๋˜์—ˆ๋˜ ๋ถ€๋ถ„ ๐Ÿค”
refreshControl์˜ Handling : .endRefreshing ์„ ์‹คํ–‰์‹œํ‚ค๋Š” ํƒ€์ด๋ฐ
UITableView : delegate, dataSource, customCell / ๋ฅผ ์–ด๋–ป๊ฒŒํ•ด์•ผํ•˜๋‚˜ !
–  peoples๊ณผ reload ์˜ binding : ์–ด๋Š๊ฑธ ๊ธฐ์ค€์œผ๋กœ ๋ฐ”์ธ๋”ฉ์„ ํ•  ๊ฒƒ์ธ๊ฐ€
– URLSession ๊ด€๋ จ : request ๊ฒฐ๊ณผ๋ฌผ์„ Observable๋กœ return์‹œํ‚ค๋Š”๊ฒŒ ๋‚˜์€๊ฐ€? ์•„๋‹ˆ๋ฉด ๊ทธ๋ƒฅ ๋ฐฐ์—ด์œผ๋กœ? Rx์˜์กด๋„์˜ ๋ฌธ์ œ.

2) ๊ณผ์ œ
– VC์˜ binding์ด ๋๋‚˜์ž๋งˆ์ž ๋ณ„๋„์˜ ์œ ์ € ๋™์ž‘ ์—†์ด ๋ฐ”๋กœ requestํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์–ด๋–ป๊ฒŒํ•ด์•ผ RxSwift์Šค๋Ÿฌ์šด ์ฝ”๋“œ๊ฐ€ ๋ ๊นŒ.

3) ๋จธ๋ฆฟ์† ์ •๋ฆฌ๊ฐ€ ์™„๋ฒฝํ•˜์ง€ ์•Š์€ ๋ถ€๋ถ„
– Observable๊ณผ Observer
– Observable<ํƒ€์ž…>.create ~~~~ ์ด ๋ถ€๋ถ„์„ ์“ฐ๋Š”๊ฒŒ ์–ด๋ ต๋‹ค ใ… ใ…กใ… 
– Disposable
– Subject VS Observable
– bind VS subscribe : error, next, complete ๋ฅผ ์„ฌ์„ธํ•˜๊ฒŒ ํ•ธ๋“ค๋งํ•  ๊ฒƒ์ธ๊ฐ€?
– Drive : UI ์ค‘์‹ฌ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๋“ฏ?
– MainThread ๋Œ€์‹ ์— MainScheduler ์‚ฌ์šฉํ•˜๊ธฐ. ๊ฐˆ์•„ํƒ€๋Š” ๊ฒƒ๋„!



๋‹ค์‹œ ํ•œ ๋ฒˆ ์งš๊ณ  ๋„˜์–ด๊ฐ€๊ธฐ

1. Observable๊ณผ Observer
Observer = ๊ด€์ฐฐ์ž. Observable = ๊ด€์ฐฐ์ž๋ฅผ ๊ฐ€์ ธ event๋ฅผ ๊ตฌ๋….
Observer๊ฐ€ ๋ฐœํ–‰์‹œํ‚จ ์ด๋ฒคํŠธ๋“ค์„ Observable์ด ๊ตฌ๋…(subscribe)
– Observable์ด ์ด๋ฒคํŠธ๋ฅผ ๋ฐฐ์ถœํ•˜๋ฉด Observer๊ฐ€ ๋ฐ˜์‘.



2. Subject VS Observable
– Subject = Observable + Observer
ํ•˜๋‚˜์˜ Observer๋ฅผ ์—ฌ๋Ÿฌ ๊ณณ์—์„œ ๊ตฌ๋…ํ–ˆ์„ ๋•Œ ์ฐจ์ด๊ฐ€ ํฌ์ธํŠธ !
Subject : ์—ฌ๋Ÿฌ ๊ณณ์—์„œ ๊ตฌ๋…ํ•ด๋„ Observer์˜ ์ด๋ฒคํŠธ ๊ฐ’์ด ๋™์ผ. (ๅคš๊ตฌ๋…์ž : ๋‹จ์ผObserver)
– ObserverType ์ด๊ธฐ ๋•Œ๋ฌธ์— (Observer์˜ ํŠน์„ฑ) on ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
Observable : ๊ฐ๊ฐ์˜ ๊ตฌ๋…์— ๋Œ€ํ•œ Observer๊ฐ€ ์กด์žฌ. (๋‹จ์ผ๊ตฌ๋…์ž : ๋‹จ์ผObserver)



3. Observable<ํƒ€์ž…>.create ~~~~ ์ด ๋ถ€๋ถ„์„ ์“ฐ๋Š”๊ฒŒ ์–ด๋ ต๋‹ค ใ… ใ…กใ… 

public static func create(_ subscribe: @escaping (AnyObserver<Element>) -> Disposable) -> Observable<Element> {
    return AnonymousObservable(subscribe)
}

subscribe
@escaping (AnyObserver) -> Disposable
– event๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋Š” Obsever๊ฐ€ ์ฃผ์–ด์ง€๊ณ  Disposable์„ ๋ฆฌํ„ด์‹œ์ผœ์•ผํ•˜๋Š” ํด๋กœ์ €.

/// Represents a disposable resource.
public protocol Disposable {
    /// Dispose resource.
    func dispose()
}


– Disposable
Disposables : Disposable ์„ ์ƒ์„ฑ.
Disposable ์€ .dispose ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค. ์ฆ‰, Observable ์„ ๋ฉ”๋ชจ๋ฆฌ์—์„œ ํ•ด์ œ์‹œํ‚ค๊ณ  ํ•ด์ œํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ค€๋‹ค.
.dispose : ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํ•ด์ œ์‹œํ‚ค๋Š” ๋ฉ”์†Œ๋“œ
– DisposeBag์— ๋‹ด๊ธฐ๊ฒŒ ๋œ๋‹ค. DisposeBag์ด dealloc๋˜๋ฉด ๊ฐ™์ด delloc ๋œ๋‹ค.



4. bind VS subscribe : error, next, complete ๋ฅผ ์„ฌ์„ธํ•˜๊ฒŒ ํ•ธ๋“ค๋งํ•  ๊ฒƒ์ธ๊ฐ€?

    public func bind(to relays: PublishRelay<Element>...) -> Disposable {
        return bind(to: relays)
    }

    public func subscribe(onNext: ((Element) -> Void)? = nil, onError: ((Swift.Error) -> Void)? = nil, onCompleted: (() -> Void)? = nil, onDisposed: (() -> Void)? = nil)
        -> Disposable {
            let disposable: Disposable
            
            if let disposed = onDisposed {
                disposable = Disposables.create(with: disposed)
            }
            else {
                disposable = Disposables.create()
            }
            
            #if DEBUG
                let synchronizationTracker = SynchronizationTracker()
            #endif
            
            let callStack = Hooks.recordCallStackOnError ? Hooks.customCaptureSubscriptionCallstack() : []
            
            let observer = AnonymousObserver<Element> { event in
                
                #if DEBUG
                    synchronizationTracker.register(synchronizationErrorMessage: .default)
                    defer { synchronizationTracker.unregister() }
                #endif
                
                switch event {
                case .next(let value):
                    onNext?(value)
                case .error(let error):
                    if let onError = onError {
                        onError(error)
                    }
                    else {
                        Hooks.defaultErrorHandler(callStack, error)
                    }
                    disposable.dispose()
                case .completed:
                    onCompleted?()
                    disposable.dispose()
                }
            }
            return Disposables.create(
                self.asObservable().subscribe(observer),
                disposable
            )
    }


.bind : error๋ฅผ ๋ณ„๋„๋กœ ํ•ธ๋“ค๋ง ํ•˜์ง€ ๋ชปํ•จ.
.subscribe : onError ์—์„œ ํ•ธ๋“ค๋ง ๊ฐ€๋Šฅ.



5. Drive : UI ์ค‘์‹ฌ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๋“ฏ?

– RxCocoa ์—์„œ ์ œ๊ณต๋จ.
Driver : UI์—์„œ ์ง๊ด€์ ์œผ๋กœ ์‚ฌ์šฉ. MainScheduler์—์„œ ์‹คํ–‰๋œ๋‹ค.
– Observable ์€ MainScheduler ์—์„œ์˜ ์‹คํ–‰์ด ๋ณด์žฅ๋˜์ง€ ์•Š๋Š”๋‹ค.
– Observable์˜ .subscribe๋Š” Driver์˜ .drive
.drive์—์„œ๋Š” onError๊ฐ€ ์ œ๊ณต๋˜์ง€ ์•Š๋Š”๋‹ค.
.asDriver : Observable ์—์„œ Driver๋กœ ๋ณ€ํ™˜



6. MainThread ๋Œ€์‹ ์— MainScheduler ์‚ฌ์šฉํ•˜๊ธฐ. ๊ฐˆ์•„ํƒ€๋Š” ๊ฒƒ๋„!

– UI ์ค‘์‹ฌ์˜ ๊ตฌ๋…์˜ ๊ฒฝ์šฐ์—๋Š” 5์˜ Driver๋ฅผ ํ™œ์šฉํ•˜๋Š”๊ฒŒ ์ค‘์š” !

public func observeOn(_ scheduler: RxSwift.ImmediateSchedulerType) -> RxSwift.Observable<Self.Element>

// ์‚ฌ์šฉ ์˜ˆ.

let observable = Observable<Int>.just(1)
observable.observeOn(MainScheduler.instance)

– ์œ„์˜ ์ฝ”๋“œ์ฒ˜๋Ÿผ MainScheduler ๋ฅผ ์ง€์ •ํ•˜๋Š” ๊ฒƒ๋„ ๋ฌผ๋ก  ๊ฐ€๋Šฅ.
– Custom Scheduler๋„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๊ณ  ์™”๋‹ค๊ฐ”๋‹คํ•˜๋Š”๊ฒƒ๋„ ๊ฐ€๋Šฅํ•˜๋‹ค.
– Scheduler ๊ฐ€ Thread๋ผ ์ƒ๊ฐํ•˜๋ฉด ์ฃ ํƒ€


“๐Ÿ“ RxSwift | MVVM with RxSwift”์˜ 1๊ฐœ์˜ ์ƒ๊ฐ

๋‹ต๊ธ€ ๋‚จ๊ธฐ๊ธฐ

์•„๋ž˜ ํ•ญ๋ชฉ์„ ์ฑ„์šฐ๊ฑฐ๋‚˜ ์˜ค๋ฅธ์ชฝ ์•„์ด์ฝ˜ ์ค‘ ํ•˜๋‚˜๋ฅผ ํด๋ฆญํ•˜์—ฌ ๋กœ๊ทธ ์ธ ํ•˜์„ธ์š”:

WordPress.com ๋กœ๊ณ 

WordPress.com์˜ ๊ณ„์ •์„ ์‚ฌ์šฉํ•˜์—ฌ ๋Œ“๊ธ€์„ ๋‚จ๊น๋‹ˆ๋‹ค. ๋กœ๊ทธ์•„์›ƒ /  ๋ณ€๊ฒฝ )

Google photo

Google์˜ ๊ณ„์ •์„ ์‚ฌ์šฉํ•˜์—ฌ ๋Œ“๊ธ€์„ ๋‚จ๊น๋‹ˆ๋‹ค. ๋กœ๊ทธ์•„์›ƒ /  ๋ณ€๊ฒฝ )

Twitter ์‚ฌ์ง„

Twitter์˜ ๊ณ„์ •์„ ์‚ฌ์šฉํ•˜์—ฌ ๋Œ“๊ธ€์„ ๋‚จ๊น๋‹ˆ๋‹ค. ๋กœ๊ทธ์•„์›ƒ /  ๋ณ€๊ฒฝ )

Facebook ์‚ฌ์ง„

Facebook์˜ ๊ณ„์ •์„ ์‚ฌ์šฉํ•˜์—ฌ ๋Œ“๊ธ€์„ ๋‚จ๊น๋‹ˆ๋‹ค. ๋กœ๊ทธ์•„์›ƒ /  ๋ณ€๊ฒฝ )

%s์— ์—ฐ๊ฒฐํ•˜๋Š” ์ค‘