Search code examples
swiftsequencefuturecombinepublisher

Make a sequence of 2 requests with Combine in Swift?


I have the following code:

import Combine

func login() -> Future<Token, Error> { ... }
func updateImage() -> Future<Status, Never> { ... }

login()
    .flatMap { response -> Future<Status, Error> in
        // handle response and save token locally
        return updateImage()
    }
    .sink(receiveCompletion: { error in
        ... //handleError
    }, receiveValue: { response in
        ... // handle response and a different error
    })
    .store(in: ...)

I want to join login() and updateImage() into a sequence. However, I found that zip is only for parallel requests. I need a way to chain them sequentially, and I am not sure if map, tryMap, or mapError methods can help me achieve that. I need to not only transform but also handle the response data and errors from each function.

How to solve this issue?


Solution

  • In iOS 14+, there is a new overload of flatMap that allows you flat map to a publisher that Never fails, so you just need to change the explicit return type of your closure to match the type that updateImage returns:

    .flatMap { response -> Future<Status, Never> in // change "Error" to "Never"
    

    Prior to iOS 14, you would need to use setFailureType(to:):

    .flatMap { response -> Publishers.SetFailureType<Future<Status, Never>, Error> in
        // ...
        return updateImage().setFailureType(to: Error.self)
    }
    

    If you don't like such a long type name, use AnyPublisher:

    .flatMap { response -> AnyPublisher<Status, Error> in
        // ...
        return updateImage().setFailureType(to: Error.self).eraseToAnyPublisher()
    }