Apple์—์„œ ๊ณต๊ฐœํ•œ API์ธ Combine. ์ด์ œ Rx์—์„œ Combine์œผ๋กœ ๋งŽ์ด ๋„˜์–ด๊ฐ„๋‹ค๊ณ  ํ•œ๋‹ค. ๋ฌด์—‡์„ ํ•˜๋Š” ๊ฒƒ์ธ์ง€, ์™œ ์ข‹์€์ง€, ๋ฐ”๋กœ ์จ๋จน๊ธฐ ์œ„ํ•œ ํŒ์€ ๋ฌด์—‡์ด ์žˆ๋Š”์ง€ ์•Œ์•„๋ณธ๋‹ค.

์˜์˜

  • Rx์˜ ์‚ฌ์ „์  ์˜๋ฏธ
    • Reactive (Observer pattern)
    • Functional
  • ๊ฐ’์„ ๋‹ค๋ฃจ๋Š” ๋ฐฉ์‹
    • ๊ธฐ์กด
      • ์‹œ๊ฐ„์ด ์ง€๋‚จ์— ๋”ฐ๋ผ ๋ณ€์ˆ˜ ๊ฐ’์ด ๋ณ€ํ•œ๋‹ค.
      • a = 3 โ†’ a = 4
    • Rx
      • ๊ฐ’๋“ค์˜ sequence(stream)
      • ์ผ์ข…์˜ array๋ฅผ ๋‹ค๋ฃจ๋Š” ์‚ฌ๊ณ ๋ฐฉ์‹
  • ๊ธฐ๋ณธ ํ๋ฆ„
    • Publisher
      • ๊ฐ’์„ ๋ฐฉ์ถœ
    • Operator
      • Publisher๋“ค์„ ์กฐํ•ฉ
    • Subscriber
      • ๋ฐฉ์ถœ๋œ ๊ฐ’์„ ๋ฐ›์•„์„œ ์‚ฌ์šฉ
class ViewModel {
    let text: AnyPublisher<String, Error>
}
 
class ViewController: UIViewController {
    let label = UILabel()
    var viewModel = ViewModel()
 
    init() {
        self.viewModel.text
            .assign(to: \.text, on: label) // label, text์— assignํ•˜๊ฒ ๋‹ค
    }
}

Publisher

๋ฐฉ์ถœํ•˜๋Š” Event ์ข…๋ฅ˜

  • Output Value
    • ํŠน์ • ๊ฐ’
    • ํŠน์ • ๊ฐ’์„ ํฌํ•จํ•˜๋Š” Event (Publisher๊ฐ€ ๋  ์ˆ˜๋„ ์žˆ์Œ)
  • Successful Complete
    • ํŠน์ • Publisher๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ๋๋‚œ ๊ฒฝ์šฐ ๋ฐฉ์ถœํ•˜๋Š” ์ด๋ฒคํŠธ
    • โ€”eventโ€”eventโ€”eventโ€”|โ†’
      • |์— ํ•ด๋‹นํ•˜๋Š” ๊ฒƒ
    • Complete์ดํ›„์—๋Š” ํ•ด๋‹น Publisher๋Š” ๊ฐ’์„ ๋ฐฉ์ถœํ•  ์ˆ˜ ์—†์Œ
  • Complete with Error
    • Error๋ฅผ ๋ฐœ์ƒํ•˜์—ฌ Publishing์ด ๋๋‚œ ๊ฒฝ์šฐ
    • โ€”eventโ€”eventโ€”ERRORโ‡’

Publisher์˜ ์ •์˜

let publisher: AnyPublisher<ValueType, ErrorType>

publisher๋Š” type 2๊ฐ€์ง€๋ฅผ ์ •์˜ํ•ด์•ผ ํ•œ๋‹ค. ValueType๊ณผ ErrorType์ด๋‹ค.

Value Type

  • output value event
  • ์–ด๋– ํ•œ ํƒ€์ž…์ด๋“  ๊ฐ€๋Šฅ
    • Int, class, enum
    • array, tuple
    • publisher (์ฆ‰ publisher์˜ publisher๋„ ๊ฐ€๋Šฅ, ์ƒ๊ฐ๋ณด๋‹ค ๋งŽ์ด ์“ฐ์ž„)
      typealias InnerPublisher = AnyPublisher<Int, InnerError>
      let outterPublisher = AnyPublisher<InnerPublisher, OutterError>

Error Type

  • complete with error event
  • Swift.Error๋ฅผ ์ฑ„ํƒํ•ด์•ผ ํ•จ
  • complete with Error event ๋ฐฉ์ถœ์‹œ ๋ฐ˜๋“œ์‹œ ์ •ํ•œ Error๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.
  • compiler๊ฐ€ ๋ณด์žฅํ•œ๋‹ค. (ํ‹€๋ฆฌ๋ฉด ๋นŒ๋“œ ์—๋Ÿฌ)
  • Error๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด Never๋„ ๊ฐ€๋Šฅ

Publisher ์ƒ์„ฑ ๋ฐฉ๋ฒ•

Notification

let myNotification = Notification.name("MyNotification")
let publisher = NotificationCenter.default.publisher(for: myNotification, object: nil)

ํŠน์ • ์ฝ”๋“œ์—์„œ NotificationCenter.default.post() ํ˜ธ์ถœ ์‹œ๋งˆ๋‹ค, ๋ณ€์ˆ˜๋กœ ์„ ์–ธํ•œ publisher์—์„œ notification์ด ๋ฐฉ์ถœ๋œ๋‹ค.

Array, Range

[1, 2, 3].publisher
["a", "b", "c"].publisher
(1...6).publisher

์šฐ๋ฆฌ๊ฐ€ ๊ธฐ์กด์— ์‚ฌ์šฉํ•˜๋Š” ์ž๋ฃŒ๊ตฌ์กฐ ๋Œ€๋ถ€๋ถ„์ด publisher๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

Just

Just(3)
Just("abc")

Just๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ•ด๋‹น ๊ฐ’ ํ•˜๋‚˜๋งŒ ๋ฐฉ์ถœํ•˜๋Š” publisher๊ฐ€ ๋งŒ๋“ค์–ด์ง„๋‹ค. publisher๋ฅผ ์กฐํ•ฉํ•  ๋•Œ, ์ค‘๊ฐ„์ค‘๊ฐ„์— ๋งŽ์ด ์‚ฌ์šฉ๋œ๋‹ค.

Future

let publisher = Future<ValueType, ErrorType> { result in
    // write code
    // API ํ˜ธ์ถœ ๋“ฑ์˜ ์ž‘์—…ํ›„ ์„ฑ๊ณต ์—ฌ๋ถ€์— ๋”ฐ๋ผ..
    if success {
        result(.success(๋ฐฉ์ถœํ•œ ๊ฐ’))
    } else {
        result(.failure(error))
    }
}

URLSession

URLSession.shared.dataTaskPublisher(for: url)
  • url๋กœ ๋ถ€ํ„ฐ data๋ฅผ ๋ฐ›์•„์˜จ ํ›„ ๊ทธ data๋ฅผ ๋ฐฉ์ถœํ•˜๋Š” publisher
  • ์œ„ ์˜ˆ์‹œ๋“ค๊ณผ ๊ฐ™์ด ๊ฐ’์„ ๋งŒ๋“ค์–ด๋‚ด๋Š” ๊ธฐ์กด library๋“ค์ด publisher๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

KVO

  • KVO ๋งŒ์กฑํ•˜๋Š” class๋“ค, ์ฆ‰ NSObject๋ฅผ ์ƒ์†๋ฐ›๋Š” ๊ฒƒ๋“ค, UIKit์—์„œ ์ •์˜ํ•˜๋Š” ๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ๋„ Publisher๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๋ชจ๋“  property๊ฐ€ ๋˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค.
let view = UIView()
let publisher = view.publisher(for: \.bounds)

Subscriber

  • sink
    • closure๋กœ ์–ด๋–ค ์ฝ”๋“œ๋“  ๊ฐ€๋Šฅํ•˜๋‹ค.
  • assign
    • property์— ๊ฐ’์„ ๋„ฃ์„ ์ˆ˜ ์žˆ๋‹ค.
  • custom subscriber

sink

publisher.sink(
    receiveCompletion: { param in // ์ด์ „์— ๋ฐฐ์šด publisher์˜ completion์ด ๋„˜์–ด์˜ค๋Š” ๊ฒฝ์šฐ
        switch param {
        case .finished:
            // error ์—†์ด complete๋œ ์ƒํ™ฉ
        case .failure(let error):
            // error ๋ฐฉ์ถœ๋œ ์ƒํ™ฉ
        }
    },
    receiveValue: { value in // ๊ฐ’์ด ๋„˜์–ด์˜ค๋Š” ๊ฒฝ์šฐ
        // value ๋ฐฉ์ถœ๋œ ์ƒํ™ฉ
    }
)

assign

class MyClass {
    var value: Int
}
 
let myClass = MyClass()
 
publisher.assign(to: \.value, on: myClass)

Cancellable

  • Subscriber๊ฐ€ release(ํ• ๋‹น ํ•ด์ œ ๋˜๋Š” ๊ฒฝ์šฐ)๋˜๋ฉด publish๋Š” ์ž๋™์œผ๋กœ ์ค‘๋‹จ๋œ๋‹ค.
    • release๋˜๋Š” ์‹œ์ ์€ ์ผ๋ฐ˜ ๋ณ€์ˆ˜์˜ release ์‹œ์ ๊ณผ ๋™์ผํ•˜๋‹ค.
func method() {
    let subscriber = publisher.sink(...)
    // ๋ฉ”์†Œ๋“œ ๋‚ด์— ์žˆ์„ ๊ฒฝ์šฐ subscriber๋Š” ๊ณ„์† ๋™์ž‘ํ•œ๋‹ค.
}
// ํ•ด๋‹น ํ•จ์ˆ˜์˜ ๋™์ž‘์ด ๋๋‚œ ๊ฒฝ์šฐ local variable๋Š” release๋œ๋‹ค.
  • ํ•˜์ง€๋งŒ ์ด๋Ÿฐ ๊ฒƒ์€ ์šฐ๋ฆฌ๊ฐ€ ์›ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋‹ค.
  • method์—์„œ returnํ•˜๋”๋ผ๋„ subscriber๊ฐ€ ๊ณ„์† ๋™์ž‘ํ•ด์•ผ ํ•œ๋‹ค.
  • ๊ณ„์†ํ•ด์„œ ๊ฐ’์„ ๋ฐ›์•„์™€์„œ ์—…๋ฐ์ดํŠธํ•˜๊ธธ ์›ํ•˜๊ธฐ ๋•Œ๋ฌธ
  • ์–ด๋–ป๊ฒŒ ํ•  ์ˆ˜ ์žˆ์„๊นŒ? ์ด๋Ÿฐ ๊ฒฝ์šฐ ๋ณดํ†ต class์•ˆ์— member ๋ณ€์ˆ˜๋กœ ๊ฐ€์ง€๊ณ  ์žˆ๋Š”๋‹ค.

์ž๋™ ์ค‘๋‹จ์„ ๋ง‰๋Š” ๋ฐฉ๋ฒ•

class A {
    var mySubscriber: AnyCancellable?
 
    func method() {
        self.mySubscriber = publisher.sink(...)
        // ๋ฉ”์†Œ๋“œ๋ฅผ ๋ฒ—์–ด๋‚˜๋„ release ๋˜์ง€ ์•Š์Œ
    }
    
    func otherMethod() {
        self.mySubscriber?.cancel() // ํŠน์ • ์ƒํ™ฉ์—์„œ ๋™์ž‘์„ ๋ฉˆ์ถ”๊ณ  ์‹ถ๋‹ค๋ฉด cancelํ•˜๊ณ  ๋Œ€์ฒดํ•จ
    }
}
  • ๋ชจ๋“  subscriber๋Š” cancellableํ•˜๋‹ค.
  • ํ•˜์ง€๋งŒ ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์‚ฌ์šฉํ•˜๋Š” ๋ชจ๋“  Subscriber๋ฅผ ๋‹ค ๋“ค๊ณ  ์žˆ์–ด์•ผ ํ•œ๋‹ค.
  • ํŠน๋ณ„ํžˆ ํŠน์ • Subscriber๋ฅผ cancelํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด ๋ณ€์ˆ˜๋กœ๊นŒ์ง€ ๋“ค๊ณ  ์žˆ์„ ํ•„์š”๋Š” ์—†๋‹ค.

Subscriber๋“ค์„ ํ•˜๋‚˜์˜ ๋ฉค๋ฒ„๋ณ€์ˆ˜์— ๋ชจ๋‘ ๋„ฃ๊ธฐ

class A {
    var subscriptions = Set<AnyCancellable>()
 
    func method() {
        let subscriber = publisher.sink(...)
        subscriver.store(in: &subscriptions)
    }
    // class instance๊ฐ€ release๋˜๋ฉด ๋ชจ๋‘ release๋œ๋‹ค.
}

์ผ๋ฐ˜์ ์ธ ์ฝ”๋“œ์˜ ๋ชจ์Šต

class A {
    var subscriptions = Set<AnyCancellable>()
 
    func someMethod() {
        self.viewModel.aPublisher
                        .operator()
                        .sink(...)
                        .store(in: &subscriptions)
    }
}

Subject

  • ์ˆ˜๋™์œผ๋กœ ๊ฐ’์„ ๋ฐฉ์ถœ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋Š” publisher
let subject = PassthroughSubject<Int, Error>
 
subject.sink(...) // Publisher์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ Subscriber ๋“ฑ๋ก
subject.send(value) // ๊ฐ’ ๋ฐฉ์ถœ ๊ธฐ๋Šฅ ์ถ”๊ฐ€

PassthroughSubject

  • ๋ฐฉ์ถœ๋งŒ ํ•˜๊ณ , ๊ฐ’์„ ๋ณด๊ด€ํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • ์ƒˆ๋กœ Subscribe๋˜๋Š” ์ˆœ๊ฐ„ ๋ณ„๋‹ค๋ฅธ ๋™์ž‘์„ ํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • ๊ฐ’์„ ๋ฐฉ์ถœํ•œ ํ›„์—๋Š” ์ €์žฅํ•˜์ง€ ์•Š๊ณ  ๋ฒ„๋ฆฐ๋‹ค.
  • ๊ฐ’์„ ์ €์žฅํ•  ํ•„์š”๊ฐ€ ์—†๋Š” publisher๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.
  • ํ˜น์€ ์ €์žฅํ•˜๋ฉด ๋ฌธ์ œ์˜ ์—ฌ์ง€๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ ์‚ฌ์šฉํ•œ๋‹ค.

CurrentValueSubject

  • ๋งˆ์ง€๋ง‰์œผ๋กœ ๋ฐฉ์ถœํ–ˆ๋˜ ๊ฐ’ 1๊ฐœ๋ฅผ ๋ณด๊ด€ํ•œ๋‹ค.
  • ์ƒˆ๋กœ Subscribe๋  ๋•Œ๋งˆ๋‹ค ๋ณด๊ด€ํ–ˆ๋˜ ๊ฐ’์„ ๋ฐฉ์ถœํ•œ๋‹ค.

  • ๋งˆ์ง€๋ง‰์œผ๋กœ ๋ฐฉ์ถœํ–ˆ๋˜ ๊ฐ’์„ ์ˆ˜๋™์œผ๋กœ ์–ป์„ ์ˆ˜๋„ ์žˆ๋‹ค.

    let subject = CurrentValueSubject<Int, Error>(0)
    subject.send(๊ฐ’1)
    subject.send(๊ฐ’2)
     
    let val = subject.value // ์ €์žฅ๋œ ๊ฐ’์„ ์–ป์Œ
    • ๋ณ€์ˆ˜์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฉด์„œ Publisher๋กœ ์‚ฌ์šฉ๋„ ๊ฐ€๋Šฅํ•˜๋‹ค.
    • ํ˜„์žฌ ์ƒํƒœ๊ฐ’์„ ์ €์žฅํ•˜๋Š” ์šฉ๋„๋กœ ์ข‹๋‹ค.

Subject์˜ ๋‹จ์ 

Combine์€ ์• ์ดˆ์— ๋ณ€ํ™”ํ•˜๋Š” ๊ฐ’๋“ค์˜ sequence๋ฅผ ๋‹ค๋ฃจ๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ ๋‚˜์˜จ ํ”„๋ ˆ์ž„์›Œํฌ์ด๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ’์ฒ˜๋Ÿผ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ๋Š” Subject๋ฅผ ๋‚จ์šฉํ•˜๋ฉด, Combine์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ์ฝ”๋“œ์™€ ๋น„์Šทํ•œ ํ˜•ํƒœ๊ฐ€ ๋  ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ๋‚จ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ์ข‹๋‹ค. ๊ฐ€์žฅ ๋‚˜์ค‘์— ๊ณ ๋ คํ•˜๋Š” ์šฉ๋„๋กœ ๋‘๋ฉด ์ข‹์„ ๊ฒƒ์ด๋‹ค.

Operator

์ˆ˜ํ•™์˜ operator๋Š” ์ˆซ์ฐจ๋ฅผ ์กฐํ•ฉํ•ด์„œ ์ƒˆ๋กœ์šด ์ˆซ์ž๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด๋‹ค. ์—ฐ์ด์–ด์„œ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.(์—ฐ์‚ฐ์ž ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๊ฐ™์€ ๊ฒฝ์šฐ)

Combine์—์„œ Operator๋Š” Publisher๋ฅผ ์กฐํ•ฉํ•˜์—ฌ ์ƒˆ๋กœ์šด Publisher๋ฅผ ๋งŒ๋“ ๋‹ค๋ผ๊ณ  ์ดํ•ดํ•˜๋ฉด ์ข‹์„ ๋“ฏํ•˜๋‹ค. ์ˆ˜ํ•™์—์„œ์˜ Operator์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์—ฐ์ด์–ด์„œ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.(chain)

publisher.oprator1().operator2(parameter: anotherPublisher)...
  • failure๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒฝ์šฐ, ๋Œ€๋ถ€๋ถ„์˜ operator๋Š” failure๋ฅผ ๊ทธ๋Œ€๋กœ ์•„๋ž˜๋กœ ์ „๋‹ฌํ•œ๋‹ค.
  • (๋ฌผ๋ก  failure๋ฅผ ๋ณ€ํ˜•ํ•˜๋Š” operator๋„ ์žˆ๋‹ค.)

map

let publisher = intPublisher.map { $0 * 2 }
let strPublisher = pub.map { String($0) }
  • array์—์„œ์˜ map

    [10, 20].map { [$0 + 1, $0 + 2]} == [[11, 12], [21, 22]]
  • publisher์—์„œ์˜ map

    let pub1 = [10, 20].publisher
    let pub2 = pub1.map{ [$0 + 1, $0 + 2].publisher }
    // pub1์ด ๋ฐฉ์ถœํ•˜๋Š” ๊ฐ’ -> array๋กœ ๋ณ€ํ˜•ํ›„ -> ํ•ด๋‹น array๋ฅผ ๋ฐฉ์ถœํ•˜๋Š” publisher๋กœ ๋ณ€ํ™˜
    • ๊ฒฐ๊ณผ
    • type: Publisher<Publisher<Int, Error>, Error>
    • action: [11, 12].publisher ๋ฐฉ์ถœ ํ›„, [21, 22].publisher ๋ฐฉ์ถœ

tryMap

  • map๊ณผ ๊ฐ™์œผ๋‚˜ ๋‚ด๋ถ€์—์„œ error throw ๊ฐ€๋Šฅ
publisher.tryMap {
    if {
        throw myError
    }
}
 
publisher.map {
    if {
        throw myError // ๋นŒ๋“œ ์—๋Ÿฌ
    }
}
  • map์˜ ๊ฒฝ์šฐ, ๋‚ด๋ถ€์—์„œ failure๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์„ ๋ณด์žฅํ•œ๋‹ค. ์ฐธ๊ณ 
  • ๋งŒ์•ฝ publisher์—์„œ, ์ฆ‰ ์ฝ”๋“œ๋ธ”๋ฝ ๋ฐ”๊นฅ์ชฝ์—์„œ failure๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค๋ฉด, map, tryMap ๋ชจ๋‘ ๊ทธ๋Œ€๋กœ ์•„๋ž˜๋กœ ์ „๋‹ฌํ•œ๋‹ค.

flatMap

  • ์ฐธ๊ณ 

  • array์—์„œ์˜ flatMap

    [[1, 2], [3, 4]].flatMap { $0 } == [1, 2, 3, 4]
    • 2์ฐจ์› array๊ฐ€ ์žˆ์„ ๋•Œ, flatten๋จ
  • publisher์—์„œ์˜ flatMap

    let pub1: AnyPublisher<AnyPublisher<Int, Error>, Error>
    let pub2: pub1.flatMap { $0 } // type: AnyPublisher<Int, Error>
    • publisher์•ˆ์— publisher๊ฐ€ ์žˆ๋Š” ํ˜•ํƒœ
    • ์ฆ‰, publisher๋ฅผ ๋ฐฉ์ถœํ•˜๋Š” publisher
    • ์ด ๊ฒฝ์šฐ, ๋‚ด๋ถ€ type์„ ๋ฐฉ์ถœํ•˜๋Š” publisher๋กœ ๋จ

์œ„์˜ map์˜ ์˜ˆ์‹œ์™€ ๋น„๊ตํ•ด๋ณด์ž. ๋งŒ์•ฝ map์—์„œ์˜ ์˜ˆ์‹œ๋กœ ๋“  ์ฝ”๋“œ์—์„œ flatMap์œผ๋กœ ๋ณ€๊ฒฝํ•˜๋ฉด ๊ฒฐ๊ณผ๊ฐ€ ์–ด๋–ป๊ฒŒ ๋ ๊นŒ?

  • array์—์„œ์˜ map

    [10, 20].flatMap { [$0 + 1, $0 + 2]} == [11, 12, 21, 22]
  • publisher์—์„œ์˜ map

    let pub1 = [10, 20].publisher
    let pub2 = pub1.flatMap{ [$0 + 1, $0 + 2].publisher }
    // pub1์ด ๋ฐฉ์ถœํ•˜๋Š” ๊ฐ’ -> array๋กœ ๋ณ€ํ˜•ํ›„ -> ํ•ด๋‹น array๋ฅผ ๋ฐฉ์ถœํ•˜๋Š” publisher๋กœ ๋ณ€ํ™˜
    • ๊ฒฐ๊ณผ
    • type: Publisher<Int, Error>
    • action: [11, 12, 21, 22] ๋ฐฉ์ถœ

๋Œ€ํ‘œ์  ์šฉ๋„

  • ๊ฐ’์„ ๋ณ€ํ™˜ํ•ด์•ผ ํ•˜๋Š”๋ฐ, ๋ฐ˜๋“œ์‹œ publisher๋กœ ๋‚˜์™€์•ผ ํ•  ๋•Œ
.flatMap { ์‚ฌ์šฉ์ž์˜ ํ–‰๋™์œผ๋กœ ๊ฐ’์„ ๋ฐฉ์ถœํ•˜๋Š” publisher }
.flatMap { API๋ฅผ ์˜๊ณ  ์‘๋‹ต์„ ๋ฐ›๋Š” Publisher }

์ด๋Ÿฐ ๊ฒฝ์šฐ ์‚ฌ์šฉํ•˜๋ฉด result๋Š” API์‘๋‹ต ๊ฐ’๋“ค์„ ๋ฐฉ์ถœํ•˜๋Š” Publisher๊ฐ€ ๋œ๋‹ค.

๋™์ž‘ ๋ฐฉ์‹

  • ๊ฐ€์žฅ ์œ—๋ผ์ธ์€ publishers๋ฅผ ๋ฐฉ์ถœํ•˜๋Š” publisher
  • flatMap์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ตœ๋Œ€ publisher๋ฅผ 2๊ฐœ๋งŒ ๊ฐ€์ง€๋„๋ก ํ–ˆ๋‹ค.
  • flatMap์€ map + flatten์ด๋‹ค.
  • ๊ทธ๋ž˜์„œ ์ผ๋‹จ map์˜ ์ ์šฉ๋ถ€ํ„ฐ ์ƒ๊ฐํ•ด๋ณด๋ฉด, ๊ฐ๊ฐ์˜ publisher๋ฅผ $0.value๋กœ ์ƒ์„ฑํ•ด์ค€๋‹ค.
    • $0.value๋Š” ๊ฐ’ ์ž์ฒด๋ฅผ ๋‚ด๋ณด๋‚ด๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๊ณ , publisher๋ฅผ ๋งŒ๋“ค์–ด์ฃผ๋Š” ํ–‰์œ„์ด๋‹ค.
    • ์ฆ‰, p1.value๊ฐ€ ์ ์šฉ๋  ๊ฒฝ์šฐ, p1์˜ 1, 4๋ฅผ ๋ฐฉ์ถœํ•˜๋Š” publisher๋ฅผ ๋งŒ๋“ค์–ด์ฃผ๊ณ 
    • p2.value๊ฐ€ ์ ์šฉ๋  ๊ฒฝ์šฐ, p2์˜ 2, 5๋ฅผ ๋ฐฉ์ถœํ•˜๋Š” publisher๋ฅผ ๋งŒ๋“ค์–ด์ค€๋‹ค.
    • ๊ทธ๋ž˜์„œ ์ƒ์„ฑ ์‹œ์ ์„ ๋ณด๋ฉด, p1, p2, p3๊ฐ€ ์ˆœ์ฐจ ์ ์šฉ๋˜๋ฉด์„œ publisher ์ƒ์„ฑ ์‹œ์ ์ด ๋ฐ€๋ฆฌ๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์ด ๋–„, max publisher๋ฅผ 2๋กœ ์žก์•˜๊ธฐ ๋•Œ๋ฌธ์—, publisher๋Š” 2๊ฐœ๋งŒ ์ฑ™๊ธด๋‹ค.
  • ๋งˆ์ง€๋ง‰์œผ๋กœ flatten์„ ์ ์šฉํ•˜์—ฌ ํ•˜๋‚˜์˜ publisher๋กœ ๋งŒ๋“ค์–ด์ค€๋‹ค.

switchToLatest

let pub1: Publisher<Publisher<Int, Error>, Error>
let pub2 = pub1.switchToLastest()
  • pub2์˜ type์€ Publisher<Int, Error>์ด๋‹ค.
    • flatMap๊ณผ ๋™์ผํ•˜๋‹ค.
  • ํ•˜์ง€๋งŒ, Publisher๊ฐ€ ๋ฐฉ์ถœ๋  ๋•Œ๋งˆ๋‹ค, ๊ทธ ์ „์— ๋ฐฉ์ถœ๋˜์—ˆ๋˜ Publisher๋Š” ๋ฌด์‹œ๋œ๋‹ค.

  • flatMap์„ ์ ์šฉํ•˜๋ฉด, publisher๋“ค์ด ๋ฐฉ์ถœํ•œ ๊ฐ’๋“ค์„ ๋ฌถ์–ด์„œ ํ•˜๋‚˜์˜ Publisher๋กœ ๋งŒ๋“ค์–ด์ฃผ์—ˆ๋‹ค.
  • ๊ทธ๋Ÿฐ๋ฐ, ๊ฐ๊ฐ์˜ Publisher๋“ค์ด ๋ฐฉ์ถœํ•˜๋Š” ์‹œ์ ์ด ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์—, ๊ฐ๊ฐ์˜ Publisher์—์„œ์˜ ๋ฐฉ์ถœํ•œ ๊ฐ’์ด ์„ž์—ฌ์„œ ์ตœ์ข… Publisher์—์„œ ๋ฐฉ์ถœ๋œ๋‹ค.
  • switchToLatest๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉด, ์ด ์‹œ์ ์—์„œ ๊ฐ€์žฅ ์ตœ๊ทผ์— Publishํ•œ Publisher๋งŒ ๊ฐ’์„ ๋ฐ˜์˜ํ•œ Publisher๋ฅผ ๋งŒ๋“ค์–ด์ค€๋‹ค.

์ผ๋ถ€ ์ œ์™ธ ์ผ๋ถ€ ๋ฐฉ์ถœ

  • ์ด๋ ‡๊ฒŒ ๋ฐฉ์ถœํ•˜๋Š” ๊ฐ’์„ ์ผ๋ถ€ ์ œ์™ธํ•˜๊ณ  ์ผ๋ถ€๋Š” ๋ฐฉ์ถœํ•˜๋Š” operator๋“ค์ด ์žˆ๋‹ค.

    • filter, compactMap
    • first, first(where:), last, last(where:)
    • drop, prefix
  • removeDuplicates

    [1, 2, 2, 3, 3, 2, 2, 4].publisher.removeDuplicates() == [1, 2, 3, 2, 4].publisher
    • ๊ฐ™์€ ๊ฐ’์ด ๋ฐ˜๋ณตํ•˜์—ฌ ๋ฐฉ์ถœ๋˜๋Š” ๊ฒƒ์„ ๋ง‰๋Š” operator

merge

  • type์ด ๊ฐ™์€ publisher๋“ค์„ ์กฐํ•ฉ
  • ๊ฐ publisher๋‚ด์—์„œ ๊ฐ’์ด ๋ฐฉ์ถœ๋˜๋ฉด, ๊ทธ๋Œ€๋กœ ๋’ค๋กœ ๋ฐฉ์ถœ์‹œํ‚ด
let result = pub1.merge(pub2, pub3)

combineLastest

  • ๊ฐ publisher ๋‚ด์—์„œ ๊ฐ’์ด ๋ฐฉ์ถœ๋˜๋ฉด, ๋‹ค๋ฅธ publisher์˜ ๋งˆ์ง€๋ง‰ ๊ฐ’๊ณผ ์กฐํ•ฉํ•˜์—ฌ tuple์„ ๋งŒ๋“ค์–ด ๋ฐฉ์ถœ
  • ์ด๋Ÿฐ ์†์„ฑ ๋•Œ๋ฌธ์— Publisher๋“ค์˜ Type์ด ๋‹ฌ๋ผ๋„ ๋œ๋‹ค. ์–ด์ฐจํ”ผ ๋ฌถ์–ด์„œ tuple๋กœ ๋ฐฉ์ถœํ•˜๊ธฐ ๋•Œ๋ฌธ

delay

  • ์ง€๊ธˆ๊นŒ์ง€๋Š” ๋ฐฉ์ถœ๋œ ์‹œ์ ์„ ๊ธฐ์ค€์œผ๋กœ action์ด ์ทจํ•ด์กŒ๋‹ค.
  • delay๊ฐ™์€ ๊ฒฝ์šฐ, ๋ฐฉ์ถœ๊ฐ’์„ ๊ธฐ์–ตํ•˜๊ณ  ์žˆ๋‹ค๊ฐ€, ์ผ์ • ์‹œ๊ฐ„ ์ดํ›„ ๋ถ€ํ„ฐ ๋ฐฉ์ถœ์„ ์‹œ์ž‘ํ•œ๋‹ค.

debounce

  • ์ผ์ • ์‹œ๊ฐ„ ๋™์•ˆ ๋ฐฉ์ถœ์ด ์—†์„ ๊ฒฝ์šฐ, ๋’ค๋กœ ๋ฐฉ์ถœ
  • ์‚ฌ์šฉ์ž๊ฐ€ ๊ธ€์ž๋ฅผ ์ž…๋ ฅํ•˜๋Š” ๊ฒฝ์šฐ ์œ ์šฉ

Reference