Ali
Ali

Reputation: 327

Swift Combine - Wait for all publishers

I am trying to use Swift combine to run many tasks with the same result. at the moment each task is a publisher that will emit a result. now I am facing a problem that I have to wait for all publishers to emit the element then moving on. kind of like dispatch group. I found zip(with:::_) operator which takes 4 publishers.

https://developer.apple.com/documentation/combine/passthroughsubject/3333571-zip

but what if you have an array of publishers (in case that they emit the same kind of element) ? is there any way to do that?

Upvotes: 19

Views: 14983

Answers (2)

Victor Sebastian
Victor Sebastian

Reputation: 197

Well you can use CombineLatest. You can input upto 3 publisher properties and have a single downstream which will be executed once all publishers are received.

As per the example given in WWDC you can use it as follows.

enter image description here

class ViewController: UIViewController {
    @IBOutlet private var termsSwitch: UISwitch!
    @IBOutlet private var privacySwitch: UISwitch!
    @IBOutlet private var nameField: UITextField!
    @IBOutlet private var submitButton: UIButton!

    @Published private var acceptedTerms: Bool = false
    @Published private var acceptedPrivacy: Bool = false
    @Published private var name: String = ""

    // Keep a reference to the subscription so it's
    // only cancelled when we are deallocated.
    private var termsStream: AnyCancellable?

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        termsStream = validToSubmit
          .receive(on: RunLoop.main)
          .assign(to: \.isEnabled, on: submitButton)

    }

    @IBAction private func acceptTerms(_ sender: UISwitch) {
        acceptedTerms = sender.isOn
    }

    @IBAction private func acceptPrivacy(_ sender: UISwitch) {
        acceptedPrivacy = sender.isOn
    }

    @IBAction private func nameChanged(_ sender: UITextField) {
        name = sender.text ?? ""
    }

    private var validToSubmit: AnyPublisher<Bool, Never> {
        return Publishers.CombineLatest3($acceptedTerms, $acceptedPrivacy, $name)
            .map { terms, pricacy, name in
                terms && pricacy && !name.isEmpty
            }.eraseToAnyPublisher()
    }

Upvotes: 1

David Pasztor
David Pasztor

Reputation: 54716

You can use MergeMany to create a single downstream receiving all emitted values from several upstreams and then call collect() on the merged publisher to emit all values at once.

let pubs = [Just(1),Just(2),Just(3)]
let downstream = Publishers.MergeMany(pubs).collect()

Upvotes: 32

Related Questions