본문 바로가기

iOS

[RxSwift] Operator (1편 - Combining Operator: SwitchLatest, Merge)

Combining Operator

SwitchLatest

🐣 두 개의 옵저버블 간에 원하는 옵저버블만 구독할 수 있게 전환하는 스위치 역할!

정의

🐣 Transforms an observable sequence of observable sequences into an observable sequence producing values only from the most recent observable sequence.

Each time a new inner observable sequence is received, unsubscribe from the previous inner observable sequence.

 

옵저버블 시퀀스들의 옵저버블 시퀀스를 가장 최근의 옵저버블 시퀀스로부터 값을 생성하는 옵저버블 시퀀스로 변형시킨다. 새로운 내부 옵저버블 시퀀스를 받을 때마다, 이전에 가지고 있던 내부 옵저버블 시퀀스를 구독해제한다.

public func switchLatest() -> Observable<Element.Element> {
    Switch(source: self.asObservable())
}
final private class Switch<Source: ObservableConvertibleType>: Producer<Source.Element> {
    private let source: Observable<Source>
    
    init(source: Observable<Source>) {
        self.source = source
    }
    
    override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Source.Element {
        let sink = SwitchIdentitySink<Source, Observer>(observer: observer, cancel: cancel)
        let subscription = sink.run(self.source)
        return (sink: sink, subscription: subscription)
    }
}

반환값 : 가장 최근에 받은 내부 옵저버블 시퀀스의 elements를 생성하는 observable 시퀀스를 반환한다.

 

옵저버블 시퀀스의 옵저버블 시퀀스? : 위 메서드의 반환형 Observable<Element.Element> 부분에서 앞 쪽의 Element가 옵저버블 시퀀스이고, 그 시퀀스의 Element를 반환하는 새로운 옵저버블을 만들겠다는 뜻!

 

쉽게 말해서, 여러 옵저버블 시퀀스들 중에서 원하는 옵저버블 시퀀스의 이벤트를 구독할 수 있도록 선택해주는 스위치 역할의 옵저버블을 만들어준다!

class SwitchLatestVC: UIViewController {
    
    private let disposeBag = DisposeBag()
    let one = PublishSubject<String>()
    let two = PublishSubject<String>()
    let three = PublishSubject<String>()
    let source = PublishSubject<Observable<String>>()

		let firstTFBehavior = BehaviorRelay<String>(value: " ")
    let secondTFBehavior = BehaviorRelay<String>(value: " ")

    lazy var switchSource = BehaviorSubject<BehaviorRelay<String>>(value: firstTFBehavior)

		// 예시 1
		private func testSwitchLatestByConsole() {
		    source
		        .switchLatest()
		        .subscribe(onNext: { value in print(value) })
		        .disposed(by: disposeBag)
		    
		    source.onNext(one) // switched one
		    one.onNext("one 1") // one 1
		    two.onNext("two 1") //
		    
		    source.onNext(two) // switched two
		    two.onNext("two 2") // two 2
		    one.onNext("one 2") //
		    
		    source.onNext(three) // switched three
		    two.onNext("two 3") //
		    one.onNext("one 3") //
		    three.onNext("three 1") // three 1
		    
		    source.onNext(one) // switched one
		    one.onNext("one 4") // one 4
		}

		// RxCocoa 적용 예시
		private func bind() {
		    
		    // MARK: SwitchLatest Operator 예시
		    
		    firstTextField.rx.text.orEmpty
		        .bind(to: firstTFBehavior)
		        .disposed(by: disposeBag)
		    
		    secondTextField.rx.text.orEmpty
		        .bind(to: secondTFBehavior)
		        .disposed(by: disposeBag)
		    
		    mySwitch.rx.isOn
		        .subscribe(onNext: { isOn in
		            isOn
		            ? self.switchSource.onNext(self.secondTFBehavior)
		            : self.switchSource.onNext(self.firstTFBehavior)
		        })
		        .disposed(by: disposeBag)
		    
		    switchSource
		        .switchLatest()
		        .map { "SwitchLatest : " + $0 }
		        .asDriver(onErrorJustReturn: "오류")
		        .drive(testLabel.rx.text)
		        .disposed(by: disposeBag)
		}
}

Merge

정의

🐣 Merges elements from all observable sequences in the given enumerable sequence into a single observable sequence.

 

모든 옵저버블 시퀀스를 병합해서 하나의 옵저버블 시퀀스처럼 합쳐준다

public func merge() -> Observable<Element.Element> {
    Merge(source: self.asObservable())
}

예시

// 아래와 같이 rx.text로 control 프로퍼티에 접근해도 되고
let observableMerge = Observable.of(firstTextField.rx.text, secondTextField.rx.text)	

observableMerge
	            .merge()
	            .map { "Merge : " + $0 }
	            .bind(to: mergeLabel.rx.text)
	            .disposed(by: disposeBag)

How to properly use switchLatest to toggle between search results and empty state of tableview?

서로 다른 자료형의 Observable을 하나로 묶기

RxSwift merge different kind of Observables

let stringSubject = PublishSubject<String>()
let stringObservable = stringSubject.asObservable().map { $0 as AnyObject }

let intSubject = PublishSubject<Int>()
let intObservable = intSubject.asObservable().map { $0 as AnyObject }

Observable.of(stringObservable, intObservable).merge()
    .subscribeNext { print($0) }
    .addDisposableTo(disposeBag)

stringSubject.onNext("a")
stringSubject.onNext("b")
intSubject.onNext(1)
intSubject.onNext(2)
stringSubject.onNext("c")