스트림을 결합하는 연산자

SteadySlower·2022년 4월 1일
0

RxSwift

목록 보기
6/7
post-thumbnail

🔮  마블 다이어그램 출처: https://reactivex.io/documentation/operators.html

스트림을 결합하는 연산자

두 개 이상의 스트림을 합쳐서 새로운 스트림을 만들어 내는 연산자들입니다.

startWith

특정 스트림의 맨 앞에 해당 데이터를 끼워넣는 연산자입니다. startWith의 인자로 전달된 값은 구독하자마자 바로 발행됩니다.

import RxSwift

let subject = PublishSubject<String>()
let disposeBag = DisposeBag()

subject
    .startWith("발행 0")
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

print("아직 발행 안함")
subject.onNext("발행 1")
subject.onNext("발행 2")
subject.onNext("발행 3")
//🖨 출력결과
발행 0 //👉 구독하는 순간에 바로 발행됨
아직 발행 안함
발행 1
발행 2
발행 3

Concat

복수의 스트림을 이어붙이는 형식입니다. 배열에 전달된 순서로 단순히 이어 붙입니다. 이전의 스트림이 completed 되기 전 까지 다음 stream이 발행으로 해도 concat은 발행하지 않습니다.

import RxSwift

let disposeBag = DisposeBag()

let subject1 = PublishSubject<String>()
let subject2 = PublishSubject<String>()
let subject3 = PublishSubject<String>()

let observable = Observable.concat([subject1, subject2, subject3])

observable
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

subject1.onNext("subject1: 발행 1")
subject1.onNext("subject1: 발행 2")
subject1.onNext("subject1: 발행 3")

subject2.onNext("subject2: 발행 1") //🚫 1이 complete 되기 전에 2 발행 (출력 안됨)
subject1.onCompleted()

subject2.onNext("subject2: 발행 2")
subject3.onNext("subject3: 발행 1") //🚫 2가 complete 되기 전에 3 발행 (출력 안됨)
subject2.onCompleted()

subject3.onNext("subject3: 발행 2")
//🖨 출력 결과
subject1: 발행 1
subject1: 발행 2
subject1: 발행 3
subject2: 발행 2
subject3: 발행 2

merge

concat과 유사하게 복수의 스트림을 하나로 합칩니다. 하지만 순서대로 합쳤던 concat과는 다르게 merge는 병렬적으로 합칩니다. 따라서 합쳐진 스트림이 발행하는 대로 merge도 발행합니다.

import RxSwift

let disposeBag = DisposeBag()

let subject1 = PublishSubject<String>()
let subject2 = PublishSubject<String>()
let subject3 = PublishSubject<String>()

let observable = Observable.merge([subject1, subject2, subject3])

observable
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

subject3.onNext("subject3: 발행 1")
subject1.onNext("subject1: 발행 1")
subject1.onNext("subject1: 발행 2")
subject2.onNext("subject2: 발행 1")
subject1.onNext("subject1: 발행 3")
subject3.onNext("subject3: 발행 2")
subject2.onNext("subject2: 발행 2")
//🖨 출력 결과 👉 merge된 순서와 관계 없이 바로바로 발행됨
subject3: 발행 1
subject1: 발행 1
subject1: 발행 2
subject2: 발행 1
subject1: 발행 3
subject3: 발행 2
subject2: 발행 2

combineLatest

복수의 스트림을 짝지어 발행합니다. 만약 짝을 지을 수 없다면 발행하지 않습니다.

한 스트림에서 데이터가 발행되면 다른 스트림의 가장 최근에 발행된 값들과 짝 지어 발행됩니다. 즉 이미 모든 스트림이 1번 이상 발행이 되었다면 어떤 스트림이든 발행되는 순간 combineLatest는 발행됩니다.

짝 짓는다는 표현을 사용했지만 3개 이상의 스트림을 합칠 수도 있습니다.

짝을 지어 발행하는 데이터의 형태를 정의하는 resultSelector라는 클로저를 가질 수 있습니다. (생략될 경우 배열의 형태로 발행됩니다.)

2개의 스트림 합치기

import RxSwift

let disposeBag = DisposeBag()

let subject1 = PublishSubject<String>()
let subject2 = PublishSubject<String>()
let subject3 = PublishSubject<String>()

let observable = Observable.combineLatest(subject1, subject2) { s1, s2 in
    "\(s1) 그리고 \(s2)"
}
//let observable = Observable.combineLatest([subject1, subject2, subject3])

observable
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

subject1.onNext("subject1: 발행 1")
subject1.onNext("subject1: 발행 2")
subject2.onNext("subject2: 발행 1") //👉 짝 지어져서 발행 시작!
subject1.onNext("subject1: 발행 3")
subject2.onNext("subject2: 발행 2")
//🖨 출력 결과
subject1: 발행 2 그리고 subject2: 발행 1 //👉 resultSelector에서 정의한 String의 모습
subject1: 발행 3 그리고 subject2: 발행 1
subject1: 발행 3 그리고 subject2: 발행 2

3개 이상 스트림 합치기

import RxSwift

let disposeBag = DisposeBag()

let subject1 = PublishSubject<String>()
let subject2 = PublishSubject<String>()
let subject3 = PublishSubject<String>()

let observable = Observable.combineLatest([subject1, subject2, subject3])

observable
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

subject3.onNext("subject3: 발행 1")
subject1.onNext("subject1: 발행 1")
subject1.onNext("subject1: 발행 2")
subject2.onNext("subject2: 발행 1") //👉 이 때 모든 스트림이 1번 이상 발행되면서 짝이 지어짐
subject1.onNext("subject1: 발행 3")
subject3.onNext("subject3: 발행 2")
subject2.onNext("subject2: 발행 2")
//🖨 출력 결과
["subject1: 발행 2", "subject2: 발행 1", "subject3: 발행 1"]
["subject1: 발행 3", "subject2: 발행 1", "subject3: 발행 1"]
["subject1: 발행 3", "subject2: 발행 1", "subject3: 발행 2"]
["subject1: 발행 3", "subject2: 발행 2", "subject3: 발행 2"]

withLatestFrom

첫 번째 스트림이 발행될 때 두 번째 스트림의 가장 최근 데이터와 짝지어 발행됩니다. 마찬가지로 짝 지을 수 없다면 발행되지 않습니다.

combineLatest와 거의 동일하지만 제 3의 스트림을 만드는 combine과는 달리 기존 스트림에 연산자로 다른 스트림을 바로 결합할 수 있습니다.

resultSelector를 정의하지 않은 경우

첫 번째 스트림은 trigger의 역할만 수행하고 두 번째 스트림의 데이터만 발행됩니다.

import RxSwift

let disposeBag = DisposeBag()

let subject1 = PublishSubject<String>()
let subject2 = PublishSubject<String>()

let observable = subject1.withLatestFrom(subject2)

subject1
    .withLatestFrom(subject2)
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

subject2.onNext("1")
subject2.onNext("2")
subject1.onNext("A")
subject2.onNext("3")
subject2.onNext("4")
subject1.onNext("B")
//🖨 출력 결과
2
4

resultSelector를 정의한 경우

정의한 클로저의 return 값을 발행합니다.

import RxSwift

let disposeBag = DisposeBag()

let subject1 = PublishSubject<String>()
let subject2 = PublishSubject<String>()

let observable = subject1.withLatestFrom(subject2)

subject1
    .withLatestFrom(subject2) { "\($0)\($1)" }
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

subject2.onNext("1")
subject2.onNext("2")
subject1.onNext("A")
subject2.onNext("3")
subject2.onNext("4")
subject1.onNext("B")
//🖨 출력 결과
A2
B4
profile
백과사전 보다 항해일지(혹은 표류일지)를 지향합니다.

0개의 댓글