본문 바로가기
RxSwift

[RxSwift] Combining Operators

by thoonk: 2022. 5. 26.

Combining Operators 에 관해 정리한 내용을 기록합니다.

 

Combining Operators 

 

startWith

요소들의 스트림을 방출하기 전에 설정한 요소를 처음으로 시작하여 스트림을 진행할 수 있다. 

현재 상태, 위치나 네트워크 연결 상태 등과 같은 데에 사용한다.

 

Observable.of(2, 3)
    .startWith(1)
    .subscribe(onNext: {
        print($0)
    })
    .disposed(by: bag)

/*
1
2
3
*/

 

concat

두 개의 스트림을 연결한 것이다.

 

let firstObservables = Observable.of(1,1,1)
let secondObservables = Observable.of(2,2)

firstObservables
    .concat(secondObservables)
    .subscribe(onNext: {
        print($0)
    })
    .disposed(by: bag)

/*
1
1
1
2
2
*/

 

concatMap 

각각의 스트림이 다음 스트림이 구독되기 전에 합쳐지는 것을 보장한다. (순서가 보장된다.)

flatMap과 concat이 합쳐진 것과 같다. 

 

let sequences = ["Number": Observable.of("1", "2", "3"),
                 "Alphabet": Observable.of("A", "B", "C")]

Observable.of("Number", "Alphabet")
    .concatMap({
        sequences[$0] ?? .empty()
    })
    .subscribe(onNext: {
        print($0)
    })
    .disposed(by: bag)

/*
1
2
3
A
B
C
*/

 

merge

여러 개의 스트림을 합치는데 순서를 보장하지 않는다. 

merge(maxConcurrent:): 합치는 스트림의 개수를 제한하기 위해서 사용하고 네트워크 요청이 많아질 때 리소스를 제한하거나 연결 수를 제한한다.

 

let odds = Observable.of(1, 3, 5, 7)
let evens = Observable.of(2, 4, 6)

Observable.of(odds, evens)
		.merge()
    .subscribe(onNext: {
        print($0)
    })
    .disposed(by: bag)

/*
1
3
2
5
4
7
6
*/

Observable.of(odds, evens)
    .merge(maxConcurrent: 1)
    .subscribe(onNext: {
        print($0)
    })
    .disposed(by: bag)

/*
1
3
5
7
2
4
6
/*

 

combineLatest

결합된 스트림은 각각의 서브 스트림에서 이벤트가 발생할 때마다 새로 결합된 요소를 방출한다.

 

let left = PublishSubject<Int>()
let right = PublishSubject<String>()

Observable.combineLatest(left, right) {
    "\($0)+\($1)"
}
.subscribe(onNext: {
    print($0)
})
.disposed(by: bag)

left.onNext(1)
right.onNext("A")
left.onNext(2)
right.onNext("B")
right.onNext("C")
right.onNext("D")
left.onNext(3)
left.onNext(4)

/*
1+A
2+A
2+B
2+C
2+D
3+D
4+D
*/

let dateDisplayFormat = Observable<DateFormatter.Style>.of(.short, .long)
let currentDate = Observable<Date>.of(Date())

Observable.combineLatest(
    dateDisplayFormat,
    currentDate,
    resultSelector: { format, date -> String in
        let dateFormatter = DateFormatter()
        dateFormatter.dateStyle = format
        return dateFormatter.string(from: date)
    }
)
.subscribe(onNext: {
    print($0)
})
.disposed(by: bag)

/*
2022/01/02
January 2, 2022
*/

 

zip

결합된 스트림은 각각의 내부 스트림들이 모두 새로운 이벤트를 방출할 때까지 기다린 후 새로 결합하여 요소를 방출한다.

 

let left = Observable.of(1, 2, 3, 4)
let right = Observable.of("A", "B", "C", "D")

Observable.zip(left, right) {
    "\($0)+\($1)"
}
.subscribe(onNext: {
    print($0)
})
.disposed(by: bag)

/*
1+A
2+B
3+C
4+D
*/

 

withLatestFrom

Trigger 역할을 하는 Observable이 요소를 방출할 때 또 다른 하나의 Observable의 가장 최신 요소를 결합하여 방출한다.

 

let button = PublishSubject<Void>()
let textdField = PublishSubject<String>()

let observable = button.withLatestFrom(textdField)
    .subscribe(onNext: {
        print($0)
    })
    .disposed(by: bag)

textdField.onNext("H")
textdField.onNext("Her")
textdField.onNext("Here")
button.onNext(())
button.onNext(())

/*
Here
Here
*/

 

sample

withLatestFrom과 동작이 비슷하지만, Trigger 역할을 하는 Observable이 여러 번 요소를 방출해도 한 번만 방출한다. 

 

let button2 = PublishSubject<Void>()
let textField2 = PublishSubject<String>()

textField2.sample(button2)
    .subscribe(onNext: {
        print($0)
    })
    .disposed(by: bag)

textField2.onNext("I")
textField2.onNext("I am")
button2.onNext(())
button2.onNext(())

 

amb

ambiguous(모호한)이란 뜻을 가지고 있다.

두 개의 스트림 중 먼저 요소를 방출하는 스트림만 방출되게 한다. (다른 스트림의 요소 방출은 무시한다.)

 

let firstSubject = PublishSubject<Int>()
let secondSubject = PublishSubject<Int>()

firstSubject.amb(secondSubject)
    .subscribe(onNext: {
        print($0)
    })
    .disposed(by: bag)

firstSubject.onNext(1)
secondSubject.onNext(20)
firstSubject.onNext(2)
secondSubject.onNext(40)
firstSubject.onNext(3)
secondSubject.onNext(60)

/*
1
2
3
/*

 

switchLatest

여러 스트림 중 가장 최신의 이벤트가 발생된 Observable로 변경하여 방출한다. 

 

let one = PublishSubject<String>()
let two = PublishSubject<String>()
let source = PublishSubject<Observable<String>>()

source
    .switchLatest()
    .subscribe(onNext: {
        print($0)
    })
    .disposed(by: bag)

source.onNext(one)
one.onNext("This is one.")
one.onNext("is This one?")
source.onNext(two)
two.onNext("This is two.")
two.onNext("is This two?")
source.onNext(one)
one.onNext("This is one.")

 

reduce

Swift에서의 reduce와 동일하게 작동한다. 

 

Observable.of(1, 2, 3, 4, 5)
    .reduce(0, accumulator: +)
    .subscribe(onNext: {
        print($0)
    })
    .disposed(by: bag)

// 15

 

scan

이전에 방출된 요소와 새로 방출된 요소를 결합하여 적용된 로직을 계속 누적한다.

매번 값이 들어올 때마다 로직이 적용된 결과 값을 방출한다.

총합, 통계, 상태를 계산할 때 등 다양하게 사용한다.

 

let nums = Observable.of(1, 2, 3, 4, 5)
let scan = nums.scan(0, accumulator: +)

Observable.zip(nums, scan) {
     "nums: \($0), scan: \($1)"
}
.subscribe(onNext: {
    print($0)
})
.disposed(by: bag)

/*
nums: 1, scan: 1
nums: 2, scan: 3
nums: 3, scan: 6
nums: 4, scan: 10
nums: 5, scan: 15
*/

 

 

부족한 점 피드백해주시면 감사합니다👍

'RxSwift' 카테고리의 다른 글

[RxSwift] Time Based Operators  (0) 2022.07.18
[RxSwift] Extension Reactive  (0) 2022.07.14
[RxSwift] Transforming Operators  (0) 2022.01.02
[RxSwift] Filtering Operators (Taking)  (0) 2022.01.01
[RxSwift] Filtering Operators (Skipping)  (0) 2021.05.11

댓글