Thanks to @fimuxd ๐ค
https://github.com/ReactiveX/RxSwift
https://github.com/fimuxd/RxSwift
RxSwift = ์๋ก์ด ๋ฐ์ดํฐ์ ๋ฐ์ํ๊ณ ์์ฐจ์ ์ผ๋ก ๋ถ๋ฆฌ๋ ๋ฐฉ์ ์ฒ๋ฆฌ. ๋น๋๊ธฐ์ ํ๋ก๊ทธ๋จ ๊ฐ๋ฐ์ ๊ฐ์ํ.
iOS์์ ์ด๋ฏธ ์ ๊ณต๋ ๋น๋๊ธฐ๋ฅผ ์งค ์ ์๋ API
– Notification Center
– Delegate Pattern
– GCD
– Closure
Section I:
Getting Started with RxSwift
๊ธฐ์ด
3๊ฐ์ง ๊ตฌ์ฑ์์ : Observables(์์ฐ์), Operators(์ฐ์ฐ์), Schedulers(์ค์ผ์ฅด๋ฌ)
1. Observables
Observable<T>
– T ํ์
์ ๋ฐ์ดํฐ shopshot์ ์ ๋ฌํ ์ ์๋ ์ด๋ฒคํธ๋ฅผ ๋น๋๊ธฐ์ ์ผ๋ก ์์ฑํ๋ ๊ธฐ๋ฅ.
– ๋ค๋ฅธ ํด๋์ค์์ ๋ง๋ ๊ฐ์ ์๊ฐ์ ๋ฐ๋ผ ์ฝ์ ์ ์๋ค.
– ์์ ๊ฐ๋ฅ ์ ํ
– next : ์ต์ , ๋ค์ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ๋ ์ด๋ฒคํธ
– completed : ์ด๋ฒคํธ๋ฅผ ์ข
๋ฃ์ํค๋ ์ด๋ฒคํธ (= ๋์ด์์ ์ด๋ฒคํธ ์์ฑ์ ์๋ค.)
– error : Observable ์ด error๋ฅผ ๋ฐ์์ํด. ์ถ๊ฐ์ ์ธ ์ด๋ฒคํธ ์์ฑ์ ์๋ค.
2. Operators
– ๋น๋๊ธฐ ์
๋ ฅ์ ๋ฐ์ ๋ถ์์์ฉ ์์ด return ์์ฑ
3. Schedulers
– Rx์ Dispatch Queue.
Architecture
– MVP, MVC, MVVM ๋ชจ๋ ๊ฐ๋ฅ. MVVM์ด ๊ฐ์ฅ ํ์ฉ์ฑ ๋์.
RxCocoa
– UI ์ปดํฌ๋ํธ์ rx ๋ฅผ ์ถ๊ฐํด์ ์ฌ์ฉ.
Observable
– Observable = observable sequence = sequence
– Observable ๋ค์ ์ผ์ ๊ธฐ๊ฐ๋์ ๊ณ์ํด์ ์ด๋ฒคํธ๋ฅผ ์์ฑํ๊ณ , ์ด๊ฑธ emitting(๋ฐฉ์ถ) ์ด๋ผ๊ณ ํ๋ค.
– ์ด๋ฒคํธ๋ค์ ์ซ์๋ ์ธ์คํด์ค ๋ฑ์ ๊ฐ์ ๊ฐ์ง ์ ์๊ณ , ํญ๊ณผ ๊ฐ์ ์ ์ค์ฒ ์ธ์๋ ๊ฐ๋ฅ.
์๋ช
์ฃผ๊ธฐ
– Observable์ ์ด๋ค ๊ตฌ์ฑ์์๋ฅผ ๊ฐ์ง๋ next ์ด๋ฒคํธ๋ฅผ ๊ณ์ํด์ ๋ฐฉ์ถ ๊ฐ๋ฅ.
– Observable์ error ์ด๋ฒคํธ๋ฅผ ๋ฐฉ์ถํด์ ์์ ์ข
๋ฃ ๊ฐ๋ฅ.
– error๋ Swift.Error
– Observable์ complete ์ด๋ฒคํธ๋ฅผ ๋ฐฉ์ถํด์ ์์ ์ข
๋ฃ ๊ฐ๋ฅ.
– instance ์์ด ๋ฐ๋ก ์ข
๋ฃ.
Observable(Observable Sequence) ์ ์
let one = 1 let two = 2 let three = 3 // just = ์ ์ํ ํ์ ์ Observable Sequence ๋ฅผ ์์ฑ. let observable: Observable<Int> = Observable<Int>.just(1) // of = ํ์ ์ถ๋ก ์ ํตํด Observable Sequence ์์ฑ. let observable2 = Observable.of(one, two, three) let observable3 = Observable.of([one, two, three]) // from = array์ ๊ฐ๊ฐ์ ์์๋ฅผ ํ๋์ฉ ๋ฐฉ์ถํ๋ Sequence. let observable4 = Observable.from([one, two, three])
– just : ํ์
๊ณผ ํจ๊ป ์ ์
– of : ํ์
์ถ๋ก
– from : Array์ ๊ฐ๊ฐ์ ๊ฐ์ ๊ฐ๊ฐ์ ์์๋ก ํ๋ Observable sequence๊ฐ ๋จ.
Observable ๊ตฌ๋
(subscribing)
– Observable์ ๊ตฌ๋
์์ด๊ธฐ ๋๋ฌธ์ ๊ตฌ๋
๋๊ธฐ ์ ์๋ ์๋ฌด๋ฐ ์ด๋ฒคํธ๋ ๋ณด๋ด์ง ์๋๋ค.
1. .subscribe()
let numbers: [Int] = [1, 2, 3] let observable = Observable.from(numbers) observable.subscribe { event in print(event) print(event.element ?? "") } // print next(1) 1 next(2) 2 next(3) 3 completed
2. .subscribe(onNext:)
observable.subscribe(onNext: { num in print(num) }, onError: { error in print(error) }, onCompleted: { print("DONE !") }) { //dispose } // print 1 2 3 DONE !
3. .empty()
let emptyValue = Observable<Void>.empty()
– ํ์ฉ ์
– ์ฆ์ ์ข
๋ฃํ ์ ์๋ Observable ์ ๋ฆฌํดํ๊ณ ์ถ์ ๋.
– ์๋์ ์ผ๋ก 0๊ฐ์ ๊ฐ์ ๊ฐ์ง๋ observable์ ๋ฆฌํดํ๊ณ ์ถ์ ๋
4. .never()
let emptyValue = Observable<Void>.never()
let disposeBag = DisposeBag() let observable = Observable<Void>.never() observable .do(onSubscribe: { print("Subscribed !") }) .subscribe(onNext: { element in print(element) }, onCompleted: { print("COMPLETED!") }) .disposed(by: disposeBag) // print๊ฒฐ๊ณผ Subscribed !
– do
5. .range()
let observable = Observable<Int>.range(start: 0, count: 5)
Disposing๊ณผ ์ข
๋ฃ
– subscrption = observable ์ด ์ด๋ฒคํธ๋ฅผ ๋ฐฉ์ถํ๋ ๋ฐฉ์์
– unsubscription = observable ์ ์๋์ ์ผ๋ก ์ข
๋ฃ = Dispose
– dispose ํ์ง ์์ผ๋ฉด ๋ฉ๋ชจ๋ฆฌ ๋์ …………… ๐ก
1. .dispose()
let subscription = observable.subscribe { event in print(event) } subscription.dispose()
2. DisposeBag()
– ๊ฐ๊ฐ์ subscription์ ๊ด๋ฆฌํ๋๊ฑด ์๋นํ ๋นํจ์จ์ .
let disposeBag = DisposeBag() subscription.disposed(by: disposeBag)
– disposable์ Dispose bag์ด ํ ๋น ํด์ ํ๋ ค๊ณ ํ ๋๋ง๋ค dispose() ๋ฅผ ํธ์ถ.
3. .create(:)
Observable<Int>.create { observer -> Disposable in observer.onNext(1) observer.onNext(2) return Disposables.create() }.subscribe(onNext: { num in print(1) }) { print("Disposed") }.disposed(by: disposeBag)
– error, complete ์์ด๋ observable sequence์ ๋์ด์ ์์ผ๋ฉด ์ข
๋ฃ๊ฐ๋๊ณ dispose ๋๋ค.
Observable Factory ๋ง๋ค๊ธฐ
– Observable Factory : ๊ฐ subscriber์๊ฒ ์๋กญ๊ฒ Observable ํญ๋ชฉ์ ์ ๊ณตํ๋ ๊ฒ. (๊ณต์ฉ์ ์ธ ๊ฐ๋
์๋.)
– .deferred : Observable์ ๋ฆฌํดํ๋ ๋ฉ์๋. subscribe ๋ ๋ ์คํ๋๋ค.
let disposeBag = DisposeBag() var isTrue = true let factory: Observable<Int> = Observable.deferred { isTrue.toggle() if isTrue { return Observable.of(1, 2, 3) } else { return Observable.of(4, 5, 6) } } for _ in 0...3 { factory .subscribe { print($0) } .disposed(by: disposeBag) }
Traits ์ฌ์ฉ
– ์ข์ ๋ฒ์์ Observable. (?????????????????)
1. Single
let observable = Observable<Int>.just(0) observable.asSingle() let subject = PublishSubject<Int>() subject.asSingle()
– ๋ ๊ฐ์ง ์ด๋ฒคํธ ๋ฐฉ์ถ : .success(value), .error
– .success(value) = .next + .completed
– ์ฌ์ฉ ์ : ์ฑ๊ณต, ์คํจ๋ก ํ์ธ๋ ์ ์๋ 1ํ์ฑ ํ๋ก์ธ์ค(๋ฐ์ดํฐ ๋ค์ด๋ก๋ ๋ฑ)
2. Completable
let completable = Completable.create(subscribe: (@escaping Completable.CompletableObserver) -> Disposable)
– ๋ ๊ฐ์ง ์ด๋ฒคํธ ๋ฐฉ์ถ : .completed, .error
– observable์ completable๋ก ๋ณ๊ฒฝ์ ๋ถ๊ฐ๋ฅ.
– ์ฌ์ฉ ์ : ์ฐ์ฐ์ด ์ ๋๋ก ์๋ฃ๋์๋์ง๋ง ํ์ธํ ๋.
3. Maybe
let observable = Observable<Int>.just(0) observable.asMaybe() let subject = PublishSubject<Int>() subject.asMaybe()
– 1. Single + 2. Completable
– .success(value), .completed, .error. 3๊ฐ์ง ๋ชจ๋ ๋ฐฉ์ถ ๊ฐ๋ฅ.
– .complete ๋๋๋ผ๋ ์๋ฌด ๊ฐ์ ๋ฐฉ์ถํ์ง ์๋๋ค.
Debug
let disposeBag = DisposeBag() let observable = Observable<Void>.never() observable .debug("DEBUG") .subscribe() .disposed(by: disposeBag) // ์คํ ๊ฒฐ๊ณผ 2020-05-28 17:00:40.348: DEBUG -> subscribed 2020-05-28 17:00:40.350: DEBUG -> isDisposed
Subjects
์ค์๊ฐ์ผ๋ก Observable์ ์๋ก์ด ๊ฐ์ ์ถ๊ฐํ๊ณ subscriber์๊ฒ ๋ฐฉ์ถํ๋ ๊ฒ.
Observable์ด์ Observal.
Subject = Observable + Observer
– subject๋ .next ์ด๋ฒคํธ๋ฅผ ๋ฐ๊ณ , ์ด๋ฒคํธ๋ฅผ ๋ฐ์ ๋๋ง๋ค subscriber์๊ฒ ๋ฐฉ์ถํ๋ค.
– subscriber๊ฐ ์กด์ฌํ์ง ์๋ ์ด์, subject์ ์๋ก์ด ๊ฐ์ ์ถ๊ฐํด๋ ์คํ๋์ง ์๋๋ค.
Subject ์ ์ข
๋ฅ (4๊ฐ์ง)
– PublishSubject : ๋น ์ํ๋ก ์์ํ์ฌ ์๋ก์ด ๊ฐ๋ง์ subscriber์๊ฒ ๋ฐฉ์ถ.
– BehaviorSubject : ํ๋์ ์ด๊ธฐ๊ฐ์ ๊ฐ์ง ์ํ๋ก ์์. ์๋ก์ด subscriber์๊ฒ ์ด๊ธฐ๊ฐ ๋๋ ์ต์ ๊ฐ์ ๋ฐฉ์ถ
– ReplaySubject : ๋ฒํผ๋ฅผ ๋๊ณ ์ด๊ธฐํ. ๋ฒํผ ์ฌ์ด์ฆ ๋งํผ์ ๊ฐ๋ค์ ์ ์งํ๋ฉด์ ์๋ก์ด subscriber ์๊ฒ ๋ฐฉ์ถ
– Variable : BehaviorSubject ๋ฅผ ๋ํํ๊ณ ํ์ฌ์ ๊ฐ์ ์ํ๋ก ๋ณด์กด. ๊ฐ์ฅ ์ต์ /์ด๊ธฐ ๊ฐ๋ง์ ์๋ก์ด subscriber ์๊ฒ ๋ฐฉ์ถ โ BehaviorRelay
PublishSubjects
– ๊ตฌ๋
ํ๋ ์๊ฐ, ์๋ก์ด ์ด๋ฒคํธ ์์ ์ ์๋ฆฌ๊ณ ์ถ์ ๋ ์ฉ์ด.
– .complete, .error ๋ฅผ ํตํด Subject๊ฐ ์์ ์ข
๋ฃ๋ ๋๊น์ง ์ง์.
– ํ์ฉ ์ : ์๊ฐ์ ๋ฏผ๊ฐํ ๋ฐ์ดํฐ๋ฅผ ๋ชจ๋ธ๋งํ ๋.
BehaviorSubjects

– ์๋ก์ด subscriber ๋ฑ๋ก ์ , ๋ง์ง๋ง .next ์ด๋ฒคํธ๋ฅผ ์๋ก์ด subscriber ์๊ฒ ๋ฐ๋ณต์ํด.
– ๊ทธ๋์ ํ๋์ ์ด๊ธฐ๊ฐ์ด ํ์.
– ์ด๊ฑธ ์ ์ธํ๋ฉด PublishSubject ์ ์ ์ฌ.
– ํ์ฉ ์ : ํ๋ฉด์ ์๋ก Refresh์ํฌ ๋ ์ฌ์ฉํ๋ฉด ์ ์ฉ
(์ด์ ์ ๊ฐ์ฅ ์ต์ ๋ฐ์ดํฐ๋ฅผ ํ์์ํจ ํ, ์๋ก์ด ๊ฐ์ด ์ค๋ฉด ๋ฐ๋ก ํ์์ํฌ ์ ์๋ค.)
ReplaySubject
let disposeBag = DisposeBag() let subject = ReplaySubject<String>.create(bufferSize: 2) subject.onNext("1") subject.onNext("2") subject.onNext("3") subject .subscribe { print("1: ($0)") } .disposed(by: disposeBag) // print ๊ฒฐ๊ณผ 1: next(2) 1: next(3)
– bufferSize ๊ฐ์ ์ด๋ฒคํธ๋ฅผ ์ ์ฅํ๋ค๊ฐ subscribe ๋๋ฉด ์ ์ฅ๋ ์ด๋ฒคํธ๋ฅผ ๋ชจ๋ ๋ฐฉ์ถ.Variable(Deprecated)
– BehaviorSubject ์ ํ์ฌ๊ฐ์ value ํ๋กํผํฐ๋ฅผ ํตํด์ ์ ์ ์๋ค.
– .complete, .error ๋ ์ฌ์ฉํ ์ ์๋ค. value ์ ๋ณํ์ ์๊ฒผ์ ๋์ .next๋ฟ.
– ๋ฉ๋ชจ๋ฆฌ ํ ๋น์ด ํด์ ๋์์ ๋ ์๋์ ์ผ๋ก .completed
– ํ์ฉ ์ : ์ ์ ์ํ๋ฅผ ๋ํ๋ด์ผ ํ ๋. (๋ก๊ทธ์์ ๋นํ์ ๋์ ์ฒ๋ฆฌ ๋ฑ)
– BehaviorRelay๋ก ์ฌ์ฉ.
Replay (RxCocoa)
– PublishRelay, BehaviorRelay ์ ๋ ์ข
๋ฅ๊ฐ ์กด์ฌ.
– ์ฐจ์ด์ : BehaviorRelay ์์๋ ํ์ฌ๊ฐ์ ์ ์ ์๋ .value๊ฐ ์กด์ฌ.
– ๊ณตํต์ : .onNext๊ฐ ์์ด์ง๊ณ , .accept ๊ฐ ๊ฐ์ ์ญํ .
– BehaviorRelay
– .valueํตํด์ ํ์ฌ์ ๊ฐ์ ๊ฐ์ ธ์ฌ ์ ์๋ค. get-property
– .value๋ฅผ ๋ณ๊ฒฝํ๊ธฐ ์ํด์๋ .accept
– Variable์ ์ญํ ์ ์ํ.
let relay = BehaviorRelay<String>(value: "1") relay .asObservable() .subscribe { print("1: ($0)") } .disposed(by: disposeBag) relay.accept("2") relay .asObservable() .subscribe { print("2: ($0)") } .disposed(by: disposeBag) relay.accept("3")
Subject vs Relay
– subject๋ .complete, .error ๊ฐ ๋ฐ์๋๋ฉด ์ข
๋ฃ๋์ง๋ง Relay ๋ Dispose๋๊ธฐ ์ ๊น์ง ๊ณ์ ์๋ํ๋ค.
– .complete ๋ .error ๊ฐ ๋ฐ์ํ๋ฉด ์๋๋ ๊ฒฝ์ฐ. ๊ณ์ํด์ sequence๊ฐ ์์ฑ๋์ด์ผ ํ ๋ ์ฌ์ฉ.
– UIEvent์์๋ Relay๊ฐ ์ ์ .
Practice : Observables & Subjects
CODE
https://gist.github.com/unnnyong/9cf91f1849b5ab9d7facd4757359ec4f