Home > database >  Swift Combine using CombineLatest with optional publisher
Swift Combine using CombineLatest with optional publisher

Time:10-05

I am new to Combine framework so my question may be simple. I am trying to combine values from multiple publishers and send values even when received values are some publishers have not published yet.

Here is what I have for now:

return Publishers
      .CombineLatest(somePublisher, otherPublisher)
      .map { (somePublisherValues, otherPublisherValue) in
        return [
          somePublisher,
          otherPublisherValue
        ]
      }
      .removeDuplicates()
      .eraseToAnyPublisher()

Both publishers publish in all cases so it works fine. Now I need to add a third one: thirdPublisher.

  • Both somePublisher and thirdPublisher may publish
  • somePublisher may not publish
  • thirdPublisher may not publish

How can I send a response in all cases that is in previous cases something like:

  • return [somePublisher, otherPublisherValue, thirdPublisherValue]
  • return [nil, otherPublisherValue, thirdPublisherValue]
  • return [somePublisher, otherPublisherValue, nil]

I tried:

return Publishers
      .CombineLatest3(somePublisher, otherPublisher, thirdPublisher)
      .map { (somePublisherValues, otherPublisherValue, thirdPublisherValue) in
        return [
          somePublisher,
          otherPublisherValue,
          thirdPublisher
        ]
      }
      .removeDuplicates()
      .eraseToAnyPublisher()

But it does not publish as some of the publishers is not present.

CodePudding user response:

CombineLatest is designed to only emit once all of its upstream publishers have emitted their 1st value. If you want to emit as soon as any of the upstreams emitted and send nil for all missing values, you can map all of your upstreams to emit Optional values and force them to send nil before any real values using prepend(nil).

This way, you'll receive a single [nil, nil, ...] value before your real outputs (which you can ignore using dropFirst) and then you'll start receiving your desired output.

let publisher: PassthroughSubject<Int, Never> = .init()
let publisher2: PassthroughSubject<Int, Never> = .init()

let optionalPublisher = publisher.map(Optional.init).prepend(nil)
let optionalPublisher2 = publisher2.map(Optional.init).prepend(nil)

let combined = Publishers.CombineLatest(optionalPublisher, optionalPublisher2)

combined.sink {
  print("CombineLatest emitted \($0)")
}.store(in: &subscriptions)

publisher.send(1)
publisher2.send(1)
publisher.send(2)

Output:

CombineLatest emitted (nil, nil)
CombineLatest emitted (Optional(1), nil)
CombineLatest emitted (Optional(1), Optional(1))
CombineLatest emitted (Optional(2), Optional(1))
  • Related