I have a list of combine publishers that each publish one optional value. From this list, i want to create a publisher, that runs the upstream publishers in the sequence they appear in the list, one after the other, and then publish the first non-nil item i can find.
My first approach was
publishers
.publisher
.flatMap(identity)
.first(where: {$0 != nil})
but this causes all publishers to run, and the fastest to win.
I created a minimal example with a solution that comes close to what i want to achieve.
import Combine
import Foundation
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
func delayedPublisher<Value>(_ value: Value?, delay after: Double) -> AnyPublisher<Value?, Never> {
let p = PassthroughSubject<Value?, Never>()
DispatchQueue.main.asyncAfter(deadline: .now() + after) {
p.send(value)
p.send(completion: .finished)
}
return p.eraseToAnyPublisher()
}
let delays = [1,2,3,4,5].map({ Bool.random() ? nil : $0 }).shuffled()
print("Creating publishers with values and delays (in seconds)", delays)
let myPublishers = delays
.map{ delayedPublisher($0, delay: Double($0 ?? 1))
.print("\(String(describing: $0))")
.eraseToAnyPublisher() }
let cancel = myPublishers
.publisher
.flatMap { $0 }
.collect()
.map { resultList in
resultList.first(where: { $0 != nil }) ?? nil
}
.sink { result in
print("result:", result ?? "nil")
}
This creates a bunch of publishers with different delays, that may or may not produce a value. I then collect all results, and pick the first non-nil value. The problem is
I did my research but found nothing like that, perhaps because combine is not really designed to do that sort of thing. So any pointers are appreciated.
The approach is almost like your original one, but you need to restrict flatMap to run at most one publisher at a time with maxPublishers parameter:
publishers
.publisher
.flatMap(maxPublishers: .max(1), { $0 })
.compactMap { $0 } // Remove all nil values and unwrap the non-nil ones.
.first()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With