Home > OS >  Chaining zipping multiple network requests using Swift and Combine
Chaining zipping multiple network requests using Swift and Combine

Time:10-24

Overall use case is 4 network requests

  • A. Request 1 and 2 need to be in parallel and wait for both to complete
  • B. Request 3 happens after 1 and 2 complete
  • C. Request 4 happens after 3 completes

After C the final output should be of a AnyPublisher type

I am trying to use Combine to achieve the above and so far I am able to do A using Publishers.Zip and C using flatMap. What I am struggling with is B. I can do that using nested completion handlers but not using flatMap

This is the sample code with Strings (not actual code)

func overallFunc(arg1: "arg1", arg2: "arg2", arg3: "arg3", arg4: "arg4" ) -> AnyPublisher<String?, Error> {
let pub1 = func1(arg1: arg1, arg2: arg2)
let pub2 = func2(arg1: arg3, arg2: arg4)

let combinedPub = Publishers.Zip(pub1, pub2)

combinedPub
   .flatMap {(response1, response2) in
     return func3(arg1: response1.attribute1, arg2: response2.attribute2)
   }
}

The func1, func2 and func3 all return URLSession.shared.dataTaskPublisher with return type say AnyPublisher<String?, Error>

Now I am struggling with completing the code for overallFunc. The complier gives the following error around flatMap.

Type of expression is ambiguous without more context

If I add an extra return in the last line of overallFunc then the error changes to No 'flatMap' candidates produce the expected contextual result type 'AnyPublisher<String?, Error>'

In short I want to use flatMap on the result of Publishers.Zip and return another publisher so that I can add another flatMap to do the 4th request but am not able to figure out the right syntax and order of things to do that.

CodePudding user response:

The result of flatMap is going to be a String? (the result of request 3) that you then want to pass to map. That map will convert the String into the fourth request. You could then add a subscriber to the fourth request which will be the result of your overall sequence. So...

pub1
  .zip(pub2)
  .flatMap { (response1, response2) in
     func3(arg1: response1.attribute1, arg2: response2.attribute2)
  }
  .map {
     (func3Result : String?) in
      /* return publisher of 4th request */
  }
  .eraseToAnyPublisher()

The result of all of this will be the publisher of the 4th request.

CodePudding user response:

I think your attempt to obfuscate the code has removed the problem you were having. The following code compiles just fine:

func func1(arg1: String, arg2: String) -> AnyPublisher<String?, Error> { fatalError() }
func func2(arg1: String, arg2: String) -> AnyPublisher<String?, Error> { fatalError() }
func func3(arg1: String, arg2: String) -> AnyPublisher<String?, Error> { fatalError() }
func func4(arg1: String) -> AnyPublisher<String?, Error> { fatalError() }

func overallFunc(arg1: String, arg2: String, arg3: String, arg4: String) -> AnyPublisher<String?, Error> {
    Publishers.Zip(
        func1(arg1: arg1, arg2: arg2),
        func2(arg1: arg3, arg2: arg4)
    )
        .flatMap { (response1, response2) in
            func3(arg1: response1 ?? "", arg2: response2 ?? "")
        }
        .flatMap { response3 in
            func4(arg1: response3 ?? "")
        }
        .eraseToAnyPublisher()
}
  • Related