This is Understanding Combine, written by Matt Neuburg. It is a work in progress. Corrections and suggestions are greatly appreciated (you can comment here). So are donations; please consider keeping me going by funding this work at Or buy my books: the current editions are iOS 13 Programming Fundamentals with Swift and Programming iOS 13. Thank you!


.setFailureType (Publishers.SetFailureType) is sort of the opposite of .replaceError: when the upstream failure type is Never, it changes the downstream pipeline’s failure type to some sort of Error. It doesn’t actually emit any error; and in fact we are guaranteed that no error will arrive from upstream, because the upstream failure type, ex hypothesi, is Never! But in this way we give operators further downstream permission to emit an error, which they could not have done otherwise.

You do not need to use .setFailureType if you’re going to use an operator whose name begins with .try. You can always throw an error from such an operator, even if the upstream failure type is Never. For example, this code is legal (and works as expected):

    .tryMap { (i:Int) -> Int in
        if i > 2 { throw MyError.oops }
        return i
    .mapError {$0 as! MyError}

The situations where you need .setFailureType are those where you want to produce an error without saying try. For example, this code is illegal (it’s also silly, but never mind that):

    for: UIApplication.didBecomeActiveNotification)
        .timeout(0.1, scheduler:DispatchQueue.main) {MyError.oops}
        // compile error

The problem is that you can’t return an error to be sent down the pipeline if that error doesn’t match the upstream failure type. To fix that, you use .setFailureType beforehand:

        for: UIApplication.didBecomeActiveNotification)
            .setFailureType(to: Error.self)
                .timeout(0.1, scheduler:DispatchQueue.main) {MyError.oops}

I gave some more realistic examples in the discussion of .flatMap, earlier.

Table of Contents