Search

RxSwift - Combining Operator

ย Combining Operator

Combining Operator๋Š” Sequence๋“ค์„ ๋ชจ์œผ๊ณ , ๊ฐ Sequence ๋‚ด์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ‘ํ•ฉํ•˜๋Š” Operator์ž…๋‹ˆ๋‹ค.
์ฆ‰, ์„œ๋กœ ๋‹ค๋ฅธ ์ŠคํŠธ๋ฆผ์„ ํ•˜๋‚˜์˜ ์ŠคํŠธ๋ฆผ์œผ๋กœ ํ•ฉ์ณ์„œ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๊ฑฐ๋‚˜, ์—ฌ๋Ÿฌ Observable์„ ๋ฌถ์–ด์„œ ์‚ฌ์šฉํ•ด์•ผ ํ•  ๋•Œ ํ•ด๋‹น ์—ฐ์‚ฐ์ž๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.
URL Request์—์„œ ๋‘ ๊ฐœ์˜ ์‘๋‹ต์„ ๋ฐ›์•„์„œ ํ•˜๋‚˜๋กœ ๋ฌถ์–ด์„œ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•œ๋‹ค๊ฑฐ๋‚˜, ์•„์ด๋””์™€ ๋น„๋ฐ€๋ฒˆํ˜ธ ์ž…๋ ฅ์„ ๋ฐ›๊ณ , ๊ฐ๊ฐ์˜ ์ด๋ฒคํŠธ๋“ค์„ ์„ž์–ด์„œ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ํ•ด์•ผ ํ•œ๋‹ค๋ฉด Combining Operator๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํŽธํ•ฉ๋‹ˆ๋‹ค.

ย Combining Operator์˜ ์ข…๋ฅ˜

startWith

Observable์ด ์•„์ดํ…œ์„ ๋ฐœํ–‰ํ•˜๊ธฐ ์ „์— ํŠน์ • ์•„์ดํ…œ์„ ๋จผ์ € ๋ฐœํ–‰
startWith๋Š” Observable ์‹œํ€€์Šค ์•ž์— ๋‹ค๋ฅธ ๊ฐ’ ํ•˜๋‚˜๋ฅผ ์ถ”๊ฐ€์‹œ์ผœ์ค๋‹ˆ๋‹ค. ์ฒ˜์Œ ์ด๋ฒคํŠธ ๊ฐ’์„ ๋„ฃ์–ด์ฃผ๊ณ  ์‹ถ๋‹ค๋ฉด startWith๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.
let disposeBag = DisposeBag() let observable = Observable.of("ํšจ์—ฐ", "์œ ๋ฆฌ", "์ˆ˜์˜", "์œค์•„", "์„œํ˜„") observable .startWith("ํƒœ์—ฐ") .startWith("์จ๋‹ˆ") .startWith("ํ‹ฐํŒŒ๋‹ˆ") .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) /* ํ‹ฐํŒŒ๋‹ˆ ์จ๋‹ˆ ํƒœ์—ฐ ํšจ์—ฐ ์œ ๋ฆฌ ์ˆ˜์˜ ์œค์•„ ์„œํ˜„ */
Swift
๋ณต์‚ฌ

combineLatest

๋‘ Observable์—์„œ ๊ฐ๊ฐ์˜ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ, ๊ทธ ์ด๋ฒคํŠธ๋“ค ์ค‘์— ๋งˆ์ง€๋ง‰ ์ด๋ฒคํŠธ๋“ค์„ ๋ฌถ์–ด์„œ ์ „๋‹ฌ
combineLatest๋Š” ์—ฌ๋Ÿฌ Observable์—์„œ ๊ฐ€์žฅ ์ตœ์‹  ๊ฐ’์„ ๋ณ‘ํ•ฉํ•˜์—ฌ ๋ฐฉ์ถœํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ Observable ์ค‘ ํ•˜๋‚˜์˜ Observable๋งŒ ๊ฐ’์„ ๋ฐฉ์ถœํ•œ๋‹ค๋ฉด ์•„๋ฌด์ผ๋„ ์ผ์–ด๋‚˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
ํ•˜์ง€๋งŒ, ๋ชจ๋“  Observable์ด ๊ฐ’์„ ํ•œ ๋ฒˆ์”ฉ ๋ฐฉ์ถœํ–ˆ๋‹ค๋ฉด ๊ทธ ์ดํ›„์—๋Š” ๋ฐฉ์ถœํ•˜๋Š” ์ตœ์‹ ๊ฐ’์„ ๋ฐ›์Šต๋‹ˆ๋‹ค.
let disposeBag = DisposeBag() let person = PublishSubject<String>() let dog = PublishSubject<String>() Observable .combineLatest(person, dog, resultSelector: { lastPerson, lastDog in "\(lastPerson)๋„ค ๊ฐ•์•„์ง€ \(lastDog)" }) .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) person.onNext("์ฒ ์ˆ˜") person.onNext("์งฑ๊ตฌ") dog.onNext("์ฝฉ์ด") dog.onNext("ํฐ๋‘ฅ์ด") person.onNext("ํ›ˆ์ด") // person ---์ฒ ์ˆ˜---์งฑ๊ตฌ------------ํ›ˆ์ด----- // dog -------------์ฝฉ์ด---ํฐ๋‘ฅ์ด--------- /* ์งฑ๊ตฌ๋„ค ๊ฐ•์•„์ง€ ์ฝฉ์ด ์งฑ๊ตฌ๋„ค ๊ฐ•์•„์ง€ ํฐ๋‘ฅ์ด ํ›ˆ์ด๋„ค ๊ฐ•์•„์ง€ ํฐ๋‘ฅ์ด */
Swift
๋ณต์‚ฌ

withLatestFrom

ํ•œ์ชฝ Observable์˜ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ, ๋‘ ๊ฐœ์˜ Observable์„ ๋ณ‘ํ•ฉ
withLatestFrom๋Š” ํŠน์ • ํŠธ๋ฆฌ๊ฑฐ ๋ฐœ์ƒ ์‹œ, ํŠน์ • ์ƒํƒœ์˜ ์ตœ์‹  ๊ฐ’์„ ์–ป๊ณ  ์‹ถ์„ ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋ณ‘ํ•ฉํ•  ๋‹ค๋ฅธ Observable์— ์ด๋ฒคํŠธ๊ฐ€ ์—†๋‹ค๋ฉด ์ด๋ฒคํŠธ๋Š” ์Šคํ‚ต๋ฉ๋‹ˆ๋‹ค.
let disposeBag = DisposeBag() let button = PublishSubject<Void>() let textField = PublishSubject<String>() button .withLatestFrom(textField) .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) textField.onNext("ใ„ฑ") textField.onNext("๊ฑฐ") textField.onNext("๊ฒ€") textField.onNext("๊ฒ€์ƒ‰") textField.onNext("๊ฒ€์ƒ‰!") button.onNext(()) button.onNext(()) /* ๊ฒ€์ƒ‰! ๊ฒ€์ƒ‰! */
Swift
๋ณต์‚ฌ
withLatestFrom์™€ distinctUntilChanged๋ฅผ ํ•ฉ์นœ sample Operator๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉด ์œ„์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์ค‘๋ณต ๋ฌธ์ œ๋„ ๋ง‰์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
let disposeBag = DisposeBag() let button = PublishSubject<Void>() let textField = PublishSubject<String>() textField .sample(button) .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) textField.onNext("ใ„ฑ") textField.onNext("๊ฑฐ") textField.onNext("๊ฒ€") textField.onNext("๊ฒ€์ƒ‰") textField.onNext("๊ฒ€์ƒ‰!") button.onNext(()) button.onNext(()) /* ๊ฒ€์ƒ‰! */
Swift
๋ณต์‚ฌ

merge

์—ฌ๋Ÿฌ Observable์„ ํ•˜๋‚˜๋กœ ํ•ฉ์ณ์„œ ์•„์ดํ…œ์„ ๋ฐœํ–‰
merge๋Š” ์‹œํ€€์Šค๋ฅผ ํ•ฉ์ณ์ฃผ๋Š” Operator์ž…๋‹ˆ๋‹ค. mergeํ•˜๊ณ  ์žˆ๋Š” ์—ฌ๋Ÿฌ ์‹œํ€€์Šค๋“ค์„ ๋™์‹œ์— ๊ตฌ๋…ํ•˜๋ฉด์„œ ๋จผ์ € ์˜ค๋Š” ์ด๋ฒคํŠธ๋ฅผ ๋ฐ”๋กœ ๋ฐ”๋กœ ๋ฐฉ์ถœํ•ฉ๋‹ˆ๋‹ค.
๋‚ด๋ถ€ ์‹œํ€€์Šค๊ฐ€ completed๋˜๋Š” ์‹œ์ ์€ ๋ชจ๋‘ ๋…๋ฆฝ์ ์ด์ง€๋งŒ, ๋‚ด๋ถ€ ์‹œํ€€์Šค๊ฐ€ ์ข…๋ฃŒ๋˜๋ฉด merge ์‹œํ€€์Šค๋„ ์ข…๋ฃŒ๋ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ, ๋‚ด๋ถ€ ์‹œํ€€์Šค๊ฐ€ error ์ด๋ฒคํŠธ๋ฅผ ๋ฐฉ์ถœํ•˜๋ฉด merge ์‹œํ€€์Šค๋„ ์—๋Ÿฌ๋ฅผ ๋ฐฉ์ถœ์‹œํ‚ค๋ฉด์„œ ์ข…๋ฃŒ๋ฉ๋‹ˆ๋‹ค.
merge๋Š” ์—ฌ๋Ÿฌ Observable์„ ํ•˜๋‚˜๋กœ ํ•ฉ์ณ์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฒคํŠธ ํƒ€์ž…์ด ๊ฐ™์•„์•ผ์ง€๋งŒ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
let disposeBag = DisposeBag() let a = Observable.of(10, 4, 3, 2) let b = Observable.of(4, 5, 2, 1) Observable.merge(a, b) .subscribe(onNext: { print($0, terminator: " ") }) .disposed(by: disposeBag) /* 10 4 4 5 3 2 2 1 */
Swift
๋ณต์‚ฌ

zip

๋‘ Observable์˜ ๋ฐœ์ƒ ์ˆœ์„œ๊ฐ€ ๊ฐ™์€ ์ด๋ฒคํŠธ๋ฅผ ์กฐํ•ฉํ•ด์„œ ๋ฐฉ์ถœ
zip์€ combineLatest์™€ ๋น„์Šทํ•˜์ง€๋งŒ ๋‹ค๋ฅธ ๊ฐœ๋…์ž…๋‹ˆ๋‹ค. combineLatest๋Š” ๊ฐ ์ŠคํŠธ๋ฆผ์—์„œ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ๋ฐ”๋กœ ์ƒˆ๋กœ์šด ์ด๋ฒคํŠธ๊ฐ€ ๋‚ด๋ ค์˜ค์ง€๋งŒ, zip์€ ๋ชจ๋“  ์ŠคํŠธ๋ฆผ์— ๊ฐ™์€ ํšŸ์ˆ˜์˜ ์ด๋ฒคํŠธ๊ฐ€ ๋“ค์–ด์˜ฌ ๋•Œ ์ด๋ฒคํŠธ๋ฅผ ๋ฐฉ์ถœ์‹œํ‚ต๋‹ˆ๋‹ค. ์ด๋ฒคํŠธ๊ฐ€ ์Œ์„ ์ด๋ฃจ์ง€ ์•Š๋Š”๋‹ค๋ฉด ์ด๋ฒคํŠธ๋ฅผ ๋ฐฉ์ถœ์‹œํ‚ค์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
let disposeBag = DisposeBag() let flower = Observable.of("james", "nick", "thomas", "john") let basket = Observable.of("charlotte", "vicky", "ellen") Observable.zip(flower, basket) .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) /* ("james", "charlotte") ("nick", "vicky") ("thomas", "ellen") */
Swift
๋ณต์‚ฌ

switchLatest

Observable์—์„œ ๋ฐœํ–‰ํ•˜๋Š” Observable์„ ๋‹ค๋ฅธ Observable๋กœ ๋ณ€๊ฒฝํ•˜์—ฌ ์•„์ดํ…œ ๋ฐœํ–‰
๊ฐ€์žฅ ์ตœ์‹ ์˜ Observable์—์„œ ๋ฐœํ–‰ํ•˜๋Š” ์ด๋ฒคํŠธ๋ฅผ ๋ฐœํ–‰ํ•ฉ๋‹ˆ๋‹ค. Observable๋ฅผ ์„ ํƒ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๊ณ  ์‹ถ์„ ๋•Œ ์‚ฌ์šฉํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.
let disposeBag = DisposeBag() let netfilx = PublishSubject<String>() let tving = PublishSubject<String>() let remoteControl = BehaviorSubject<Observable<String>>(value: netfilx) remoteControl .switchLatest() .subscribe { print($0) } .disposed(by: disposeBag) netfilx.on(.next("D.P.")) remoteControl.on(.next(tving)) netfilx.on(.next("๋งˆ์Šคํฌ๊ฑธ")) tving.on(.next("์œ ๋ฏธ์˜์„ธํฌ๋“ค")) tving.on(.next("์ˆ ๊พผ๋„์‹œ์—ฌ์ž๋“ค")) /* next(D.P.) next(์œ ๋ฏธ์˜์„ธํฌ๋“ค) next(์ˆ ๊พผ๋„์‹œ์—ฌ์ž๋“ค) */
Swift
๋ณต์‚ฌ

concat

์—ฌ๋Ÿฌ Sequence๋ฅผ ์ˆœ์„œ๋Œ€๋กœ ๋ฌถ์–ด์คŒ
์ฒซ ๋ฒˆ์งธ Sequence๊ฐ€ ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ๊ตฌ๋…ํ•˜๊ณ  ๋‹ค์Œ Sequence๋ฅผ ๊ฐ™์€ ๋ฐฉ๋ฒ•์œผ๋กœ ๊ตฌ๋…ํ•ฉ๋‹ˆ๋‹ค.
concat์€ ์š”์†Œ๋“ค์ด ๊ฐ™์€ ํƒ€์ž…์ผ ๋•Œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๊ณ , ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์š”์†Œ๋ฅผ ์•ž ๋’ค๋กœ ์ถ”๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
let disposeBag = DisposeBag() let carnivore = Observable.of("ํ˜ธ๋ž‘์ด", "์‚ฌ์ž") let omnivore = Observable.of("๊ฐ•์•„์ง€", "์—ฌ์šฐ") let herbivores = Observable.of("์ฝ”๋ผ๋ฆฌ", "์‚ฌ์Šด") Observable .concat([carnivore, omnivore, herbivores]) .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) /* ํ˜ธ๋ž‘์ด ์‚ฌ์ž ๊ฐ•์•„์ง€ ์—ฌ์šฐ ์ฝ”๋ผ๋ฆฌ ์‚ฌ์Šด */
Swift
๋ณต์‚ฌ

reduce

๋ชจ๋“  ๊ฐ’์„ ๋”ํ•œ ๊ฐ’ ๋ฐฉ์ถœ
Swift์—์„œ ์ œ๊ณตํ•ด์ฃผ๋Š” reduce ๋ฉ”์„œ๋“œ๋กœ๋„ ๋™์ผํ•˜๊ฒŒ ๋ชจ๋“  ๊ฐ’์„ ๋”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
let disposeBag = DisposeBag() let receipt = Observable.of(10000, 23000, 150000, 40000) receipt .reduce(0, accumulator: +) .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) /* 223000 */
Swift
๋ณต์‚ฌ

scan

ํ•˜๋‚˜์”ฉ ๋”ํ•  ๋•Œ๋งˆ๋‹ค ์ฆ๊ฐ€๋œ ๊ฐ’์„ ๋ฐฉ์ถœ
scan์€ ๊ฐ’์„ ์ €์žฅํ•  ์ˆ˜ ์žˆ๊ณ , ๊ทธ ๊ฐ’์„ ํ†ตํ•ด ์ด๋ฒคํŠธ๋ฅผ ๋ณ€ํ˜•ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ณ€ํ˜•ํ•˜๋Š” ์ด๋ฒคํŠธ์˜ ํƒ€์ž…์€ ์›๋ณธ ์ด๋ฒคํŠธ์˜ ํƒ€์ž…๊ณผ ๊ฐ™์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค.
scan์€ ์ฆ๊ฐ€๋œ ๊ฐ’๋“ค์˜ ๊ธฐ๋ณธ์ด ๋  ์ดˆ๊ธฐ๊ฐ’์„ ์ง€์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
let disposeBag = DisposeBag() let observable = Observable.of("1,", "2,", "3,", "4,", "5") observable .scan("number: ") { $0 + $1 } .subscribe(onNext: { print("\($0)") }) .disposed(by: disposeBag) /* number: 1, number: 1,2, number: 1,2,3, number: 1,2,3,4, number: 1,2,3,4,5 */
Swift
๋ณต์‚ฌ