kschins
kschins

Reputation: 1224

Executing 2 parallel network requests using Swift Combine

I am trying to load data from two different endpoints using two different publishers which have different return types. I need to update the UI when both requests complete, but both requests can also fail so Zip doesn't do the trick. Usually I would use a DispatchGroup to accomplish this, but I have not figured out how to do that using Combine. Is there a way to use DispatchGroup with Combine?

let dispatchGroup: DispatchGroup = .init()
let networkQueue: DispatchQueue = .init(label: "network", cos: .userInitiated)

dispatchGroup.notify { print("work all done!" }

publisher
    .receive(on: networkQueue, options: .init(group: dispatchGroup)
    .sink { ... }
    .receiveValue { ... }
    .store(in: &cancellables)

publisher2
    .receive(on: networkQueue, options: .init(group: dispatchGroup)
    .sink { ... }
    .receiveValue { ... }
    .store(in: &cancellables)

The notify is immediately executed. Is this not the right way of doing this?

Upvotes: 2

Views: 2084

Answers (1)

Frederik
Frederik

Reputation: 404

You'll want to use the Publishers.CombineLatest which will take the two publishers and create a new publisher, with the result of the latest value from both streams:

Publishers.CombineLatest(publisher, publisher2)
    // Receive values on the main queue (you decide whether you want to do this)
    .receive(on: DispatchQueue.main)
    .sink(receiveCompletion: { completion in
        // Handle error / completion
        // If either stream produces an error, the error will be forwarded in here
    }, receiveValue: { value1, value2 in
        // value1 will be the value of publisher's Output type
        // value2 will be the value of pubslier2's Output type
    })
    // You only need to store this subscription - not publisher and publisher2 individually
    .store(in: &cancellables)

The Publishers.CombineLatest publisher, is what can be seen as the equivalent of using a DispatchGroup, where you call dispatchGroup.enter() for each network operation you initiate. However, one key difference is that the CombineLatest publisher will produce more than one value, if any of the publishers produce more than one value. For normal network operations, you don't need to worry about this. But if you find yourself in a situation where you only need the first or the first N values produces by the combined publisher, you could use the prefix(_:) modifier, which will make sure that you will never receive more than N events.

EDIT: Updated to fix typo in code.

Upvotes: 3

Related Questions