Use the flatMap operator in Combine to transform elements from a publisher to another publisher.

import Combine
import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

let idPublisher = (1...5).publisher
var cancellables = Set<AnyCancellable>()

func fetchUserDetails(userId: Int) -> AnyPublisher<String, Never> {
    let detail = "User details for \(userId)"
    return Just(detail)
        .delay(for: .milliseconds(userId * 1000), scheduler: RunLoop.main)
        .eraseToAnyPublisher()
}

print("\(Date()) : Starting")

idPublisher
    .flatMap(maxPublishers: .max(1)) { fetchUserDetails(userId: $0) }
    .sink { print("\(Date()) : \($0)") }
    .store(in: &cancellables)

In the above code:

  1. The idPublisher emits three elements: 1, 2, and 3.
  2. The fetchUserDetails function takes an Int as input and returns a publisher that emits a string.
  3. The flatMap operator transforms the elements from the idPublisher to another publisher using the fetchUserDetails function.
  4. Them maxPublishers parameter is used to specify the maximum number of publishers that can be active at a time. In this case, it is set to .max(1), which means only one publisher can be active at a time.
  5. The sink operator subscribes to the publisher and prints the emitted values.
  6. The delay operator is used to simulate a network request with a delay based on the user id.

When you run the code, you will see the following output:

2024-12-18 06:56:20 +0000 : Starting
2024-12-18 06:56:21 +0000 : User details for 1
2024-12-18 06:56:23 +0000 : User details for 2
2024-12-18 06:56:26 +0000 : User details for 3
2024-12-18 06:56:30 +0000 : User details for 4
2024-12-18 06:56:35 +0000 : User details for 5

In the above example, the maxPublishers parameter is set to .max(1), which means only one publisher can be active at a time. This ensures that the fetchUserDetails function is called sequentially for each element emitted by the idPublisher.

If the maxPublishers parameter is set to .max(2), two publishers can be active at a time, and the fetchUserDetails function will be called concurrently for two elements emitted by the idPublisher. Once one of the publishers completes, the next publisher will be activated.

In this example since we are using a delay to simulate a network request, the timing of printed values will be as follows.

  1. User details for 1 will be printed after 1 second.
  2. User details for 2 will be printed after 2 seconds, but it will start when 1 completes. So it will be printed after 3 seconds.
  3. User details for 3 will be printed after 3 seconds, but it will start when 2 completes. So it will be printed after 6 seconds.
  4. User details for 4 will be printed after 4 seconds, but it will start when 3 completes. So it will be printed after 10 seconds.
  5. User details for 5 will be printed after 5 seconds, but it will start when 4 completes. So it will be printed after 15 seconds.