[RxSwift๐Ÿฆˆ] #9 Combining Operators

๋˜์ƒยท2022๋…„ 2์›” 13์ผ
0

iOS

๋ชฉ๋ก ๋ณด๊ธฐ
14/47

1. ์•ž์— ๋ถ™์ด๊ธฐ

1. startWith(_: Item)

Observable sequence ๋งจ ์•ž์— ๊ธฐ์กด Observable ์˜ type ๊ณผ ๊ฐ™์€ type ์˜ data ๋ฅผ ๋ถ™์—ฌ์ค€๋‹ค.
ํ˜„์žฌ ์ƒํƒœ์— ์ดˆ๊ธฐ๊ฐ’์„ ๋ถ™์—ฌ์„œ ์ „๋‹ฌํ•  ๋•Œ ์‚ฌ์šฉ.

let numbers = Observable.of(2, 3, 4)
let observable = numbers.startsWith(1)

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

2. concat(_:)

2๊ฐœ์˜ sequence ๋ฅผ ๋ฌถ๋Š”๋‹ค.

1. Observable.concat(seq, seq)

let first = Observable.of(1, 2, 3)
let second = Observable.of(4, 5, 6)

let observable = Observable.concat([first, second]) // 1 2 3 4 5 6

2. seq.concat(_: seq)

์œ„์™€ ๊ฐ™์Œ. ์ด๋ ‡๊ฒŒ๋„ ์“ธ ์ˆ˜ ์žˆ๋‹ค.

let observable = first.concat(second)

3. concatMap(_: (seq) -> seq)

flatMap ๊ณผ ๋น„์Šทํ•˜์ง€๋งŒ, ์ˆœ์„œ๋ฅผ ๋ณด์žฅํ•œ๋‹ค.
๋จผ์ € ๊ตฌ๋…ํ•˜๋Š” ๊ฒƒ์˜ Observable sequence ๋ฅผ ๋ชจ๋‘ ๋ฐฉ์ถœํ•œ ๋‹ค์Œ,
๊ทธ ๋‹ค์Œ์— ๊ตฌ๋…ํ•˜๋Š” ๊ฒƒ์˜ Observable sequence ๋ฅผ ๋ฐฉ์ถœํ•œ๋‹ค.
map ์ด๋‹ˆ๊นŒ ํ•จ์ˆ˜๋„ ์ ์šฉํ•  ์ˆ˜ ์žˆ์Œ.

let sequences = [
    "Germany" : Observable.of("Berlin", "Munich", "Frankfurt"),
    "Spain" : Observable.of("Madrid", "Barcelona", "Valencia")
]

// Germany ๋ฅผ ๋จผ์ € ๊ตฌ๋…ํ•˜๊ฒŒ ๋œ๋‹ค.
let observable = Observable.of("Germany", "Spain")
    .concatMap({ country in
        sequences[country] ?? Observable.empty()
    })



2. ํ•ฉ์น˜๊ธฐ (merge)

1. seq.merge()

Observable Sequence ๊ฐ€ ๋‚ด๋ ค์˜ค๋Š” Sequence ๋ฅผ ๊ตฌ๋…ํ•œ๋‹ค.
์—ฌ๋Ÿฌ Sequence ๋ฅผ ๊ณ ๋Œ€๋กœ ๋‚ด๋ ค์„œ ํ•˜๋‚˜๋กœ ํ•ฉ์นœ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋จ. (์ ˆ๋Œ€์ ์ธ ์ˆœ์„œ)


2. merge(maxConcurrent: Observable ๊ฐœ์ˆ˜)

ํ•ฉ์น  ์ˆ˜ ์žˆ๋Š” Sequence ์˜ ์ˆ˜๋ฅผ ์ œํ•œ.
maxConcurrent ๊ฐœ์˜ Observable Sequence ๊ฐ€ ๋“ค์–ด์˜ค๊ณ  ๋˜ Observable ์ด ๋“ค์–ด์˜ค๋ฉด,
๋Œ€๊ธฐ์—ด์— ๋„ฃ์–ด๋†“๊ณ  ํ˜„์žฌ Sequence ์ค‘ ํ•˜๋‚˜๊ฐ€ ์™„๋ฃŒ๋˜๋ฉด ๋ฐ”๋กœ ๊ตฌ๋…์„ ์‹œ์ž‘.
๋งŽ์ด ์“ฐ์ง„ ์•Š์ง€๋งŒ, ๋„คํŠธ์›Œํฌ ์š”์ฒญ์ด ๋งŽ์•„์งˆ ๋•Œ์ฒ˜๋Ÿผ ์ œํ•œ์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋‹ค.

let source = Observable.of(left.asObservable(), right.asObservable())

let observable = source.merge()



3. ๊ฒฐํ•ฉํ•˜๊ธฐ (combine)

1. combineLatest(: seq, : seq, resultSelector: {(item, item) -> item})

๊ฐ Sequence ์˜ ํ˜„์žฌ ์‹œ์  ๊ฐ€์žฅ ๋งˆ์ง€๋ง‰ ๊ฒƒ์„ ํ•ฉ์นœ๋‹ค.
์–ด๋–ป๊ฒŒ ํ•ฉ์น ์ง€์— ๋Œ€ํ•œ ํ•จ์ˆ˜๋ฅผ resultSelector ์— ๋„ฃ์–ด์ฃผ๋ฉด ๋œ๋‹ค.
์ผ๋ฐ˜์ ์œผ๋กœ tuple ๋กœ ํ•ฉ์น˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๋‹ค.

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

let observable = Observable.combineLatest(left, right, resultSelector: { lastLeft, lastRight in
    return "\(lastLeft) \(lastRight)"
})

2. combineLatest([seq] ,resultSelector:)

// ์œ„์™€ ๊ฐ™์€ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜จ๋‹ค. ์ธ์ž๋ฅผ ๋ฐฐ์—ด๋กœ ์ „๋‹ฌ ๊ฐ€๋Šฅ.
let observable = Observable.combineLatest([left, right], resultSelector: { strings in
    strings.joined(separator: " ")
}

3. zip

๊ฐ Observable ์˜ ๊ฐ™์€ index ๋ผ๋ฆฌ ํ•ฉ์นœ๋‹ค.
๋‚จ๋Š”๊ฑด ๋ฒ„๋ฆผ - ๋‘ Sequence ์ค‘ ํ•˜๋‚˜๋ผ๋„ ์ข…๋ฃŒ๋˜๋ฉด zip ๋œ ๊ฒƒ๋„ ์ข…๋ฃŒ.



4. Trigger

์—ฌ๋Ÿฌ๊ฐœ์˜ Observable ๋กœ ๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์„ ๋•Œ,
์–ด๋–ค Observable ์€ ์ •๋ณด๋ฅผ ์ฃผ๋Š”๊ฒŒ ์•„๋‹ˆ๋ผ ๊ทธ์ € trigger ์—ญํ• ์„ ํ•  ์ˆ˜ ์žˆ๋‹ค.

1. trigger.withLatestFrom(_: data)

button ์„ ๋ˆ„๋ฅด๋ฉด textField ์˜ ๊ฐ€์žฅ ๋งˆ์ง€๋ง‰ ๊ฒƒ์„ ๋ฐฉ์ถœ

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

let observable = button.withLatestFrom(textField)
// ๋ฒ„ํŠผ์„ ๊ตฌ๋…ํ•˜๋Š”๋ฐ, ๋ฒ„ํŠผ์—์„œ ๋ญ๊ฐ€ ๋“ค์–ด์˜ค๋ฉด textField ์˜ ๊ฐ€์žฅ ๋งˆ์ง€๋ง‰ ๊ฒƒ์„ ๋ณด๋‚ด์ฃผ๋Š” ๊ฒƒ.

_ = observable.subscribe(onNext: { print($0) })

textField.onNext("Par")
textField.onNext("Pari")
textField.onNext("Paris")
button.onNext(()) // Paris
button.onNext(()) // Paris

2. data.sample(_: trigger)

withLatestFrom ๊ณผ ๋น„์Šทํ•˜๊ฒŒ ์ž‘๋™ํ•˜์ง€๋งŒ, trigger ๊ฐ€ ๋˜๋Š” ๊ฒƒ์€ ์—ฌ๋Ÿฌ๋ฒˆ ํ•ด๋„ ํ•œ๋ฒˆ๋งŒ ์ถœ๋ ฅ๋จ

let observable = textField.sample(button) 
// ์ˆœ์„œ๋„ ๋ฐ˜๋Œ€. textField ์˜ sample ๊ฐ’์ธ ๋ฒ„ํŠผ
// textField ์— ๋”์ด์ƒ onNext ๋กœ ๊ฐ’์ด ๋„˜์–ด์˜ค์ง€ ์•Š์„ ๋•Œ 
// button ์— onNext ๊ฐ€ ์—ฌ๋Ÿฌ๊ฐœ ๊ฐ€๋„ ํ•œ๋ฒˆ๋งŒ ์ถœ๋ ฅ๋จ.
// textField ์— ๊ฐ’์ด ๋” ๋„˜์–ด๊ฐ€๋ฉด ์ถœ๋ ฅ ๋จ
textField.onNext("Par")
textField.onNext("Pari")
button.onNext(()) // Pari
textField.onNext("Paris")
button.onNext(()) // Paris
button.onNext(())

1๊ณผ 2๋Š” ๊ฐ™์€ ์—ญํ• ์„ ํ•˜๋Š” ์ฝ”๋“œ์ด์ง€๋งŒ ์ฒด์ด๋‹์„ ๋œํ•˜๋Š”๊ฒŒ ์ข‹๋‹ค.

// 1
// trigger ๋ฅผ parameter ๋กœ ๋ฐ›์Œ
let observable = textField.sample(button) 

_ = observable

// 2
// data ๋ฅผ parameter ๋กœ ๋ฐ›์Œ
let observable2 = button.withLatestFrom(textField)

_ = observable2
    .distinctUntilChanged()



5. Switches

1. seq.amb(_: seq)

ambiguous ๋ชจํ˜ธํ•œ.
์—ฌ๋Ÿฌ sequence ์ค‘ ์–ด๋–ค ๊ฒƒ์„ ๊ตฌ๋…ํ• ์ง€ ์„ ํƒํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋งŒ๋“ ๋‹ค.
๋จผ์ € ์ด๋ฒคํŠธ๋ฅผ ๋ฐฉ์ถœํ•˜๋Š” ์ชฝ์„ ๋ฐฉ์ถœํ•˜๊ณ , ์—ฌ๋Ÿฌ sequence ์ค‘ ํ•˜๋‚˜๋ผ๋„ complete ๋˜๋ฉด completed.

let observable = left.amb(right)
// left, right ๋ฅผ ๋ชจ๋‘ ๊ตฌ๋…ํ•˜๊ณ  ์žˆ๋‹ค๊ฐ€ left right ์ค‘ event ๋ฅผ ๋จผ์ € ๋ฐฉ์ถœํ•˜๋Š” ์ชฝ๋งŒ ๊ตฌ๋…
// ๋‚˜๋จธ์ง€๋Š” ๊ตฌ๋… ๋Š์–ด๋ฒ„๋ฆผ

2. Observable<Observable>.switchLatest()

observable sequence ๋ฅผ data ๋กœ ๊ฐ€์ง€๋Š” Observable ์—์„œ
์–ด๋–ค Observable ์ด ๋‚ด๋ ค์˜ค๋ฉด ํ•ด๋‹น Observable ์„ ๊ตฌ๋…ํ•˜๊ณ  ์žˆ๋‹ค๊ฐ€,
๋‹ค๋ฅธ Observable ์ด ๋‚ด๋ ค์˜ค๋ฉด ๋‹ค๋ฅธ Observable ์„ ๊ตฌ๋…
ํ•ด๋‹น ์‹œ์ ๋ถ€ํ„ฐ์˜ event ๋“ค๋งŒ emit

let one = PublishSubject<String>()
let two = PublishSubject<String>()
let three = PublishSubject<String>()

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

let observable = source.switchLatest()
let disposable = observable.subscribe(onNext: { print($0) })

source.onNext(one) // one ์„ ๊ตฌ๋…
two.onNext("Some txt from two")
one.onNext("Some txt from one")
two.onNext("Some txt from two")

source.onNext(two) // two ๋ฅผ ๊ตฌ๋…
two.onNext("Some txt from two")
one.onNext("Some txt from one")

source.onNext(three) // three ๋ฅผ ๊ตฌ๋…
two.onNext("Why don't you see me?")
one.onNext("I'm alone, help me")
three.onNext("Hey~")

source.onNext(one) 
one.onNext("asdf")

disposable.dispose()



6. sequence ๋‚ด ์š”์†Œ๋“ค ๊ฐ„์˜ ๊ฒฐํ•ฉ

1. reduce(_: ์ดˆ๊ธฐ๊ฐ’, accumulator: ์—ฐ์‚ฐ)

์ดˆ๊ธฐ๊ฐ’์— accumulator ์— ์žˆ๋Š” ์—ฐ์‚ฐ์„ ์Œ“์•„์ค€๋‹ค.
์•ž์˜ Observable ์ด ์ข…๋ฃŒ๋˜๋Š” ์‹œ์ ์— ๊ฒฐ๊ณผ๊ฐ’๋งŒ ๋ฐฉ์ถœํ•˜๊ณ  completed


2. scan(_: ์ดˆ๊ธฐ๊ฐ’, accumulator: ์—ฐ์‚ฐ)

reduce ์ฒ˜๋Ÿผ ์Œ“๊ธด ํ•˜๋Š”๋ฐ ์—ฐ์‚ฐ ๊ณผ์ •์—์„œ ๋‚˜์˜ค๋Š” ๋‹จ๊ณ„๋ณ„ ๊ฒฐ๊ณผ๊ฐ’๋„ ๋ฐฉ์ถœํ•œ๋‹ค.



7. Chaellenge

zip ์„ ์ด์šฉํ•ด์„œ ์›๋ž˜ ๊ฐ’, scan ํ•ด์„œ ์Œ“์€ ์ดํ•ฉ์„ ๋‘˜ ๋‹ค ์ถœ๋ ฅํ•ด๋ณด์ž.

let source = Observable.of(1,3,5,7,9)

let observable2 = source.scan(0, accumulator: { summary, newValue in
    return summary + newValue
})
observable2.subscribe(onNext: { print($0) })


let newSequence = Observable.zip(source, observable2)
newSequence.subscribe(onNext: { print($0) })
profile
0๋…„์ฐจ iOS ๊ฐœ๋ฐœ์ž์ž…๋‹ˆ๋‹ค.

0๊ฐœ์˜ ๋Œ“๊ธ€