πŸ“ RxSwift | Observable, Subject, Relay

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

https://github.com/fimuxd/RxSwift/blob/master/Lectures/03_Subjects/Ch3.%20Subjects.md

– μƒˆλ‘œμš΄ 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




λ‹΅κΈ€ 남기기

μ•„λž˜ ν•­λͺ©μ„ μ±„μš°κ±°λ‚˜ 였λ₯Έμͺ½ μ•„μ΄μ½˜ 쀑 ν•˜λ‚˜λ₯Ό ν΄λ¦­ν•˜μ—¬ 둜그 인 ν•˜μ„Έμš”:

WordPress.com 둜고

WordPress.com의 계정을 μ‚¬μš©ν•˜μ—¬ λŒ“κΈ€μ„ λ‚¨κΉλ‹ˆλ‹€. λ‘œκ·Έμ•„μ›ƒ /  λ³€κ²½ )

Google photo

Google의 계정을 μ‚¬μš©ν•˜μ—¬ λŒ“κΈ€μ„ λ‚¨κΉλ‹ˆλ‹€. λ‘œκ·Έμ•„μ›ƒ /  λ³€κ²½ )

Twitter 사진

Twitter의 계정을 μ‚¬μš©ν•˜μ—¬ λŒ“κΈ€μ„ λ‚¨κΉλ‹ˆλ‹€. λ‘œκ·Έμ•„μ›ƒ /  λ³€κ²½ )

Facebook 사진

Facebook의 계정을 μ‚¬μš©ν•˜μ—¬ λŒ“κΈ€μ„ λ‚¨κΉλ‹ˆλ‹€. λ‘œκ·Έμ•„μ›ƒ /  λ³€κ²½ )

%s에 μ—°κ²°ν•˜λŠ” 쀑