Error Handling in Combine Explained
By: Antoine Van Der Lee
Tags: iOS, Swift, Programming, iOS App Development, Combine

Combine Streams and Typed Errors

A big difference between a framework like RxSwift and Combine is the requirement of typed error definitions in streams. If we compare the Observable with its Combine equivalent AnyPublisher , we can see the difference in the type declaration.

public class Observable<Element> : ObservableType

struct AnyPublisher<Output, Failure> where Failure : Error

The AnyPublisher requires us to specify the Failure error type while the Observable only takes the generic Element type.

Swift requires us to think about error handling which we can take as something good. However, it does not hold us back from defining the expected type as just Swift.Error , which basically comes down to the same behavior as in RxSwift.

Once you do require your stream to expect a certain error type, you’ll run into casting errors as each operator needs to return the same error type as the leading stream. Let’s dive into the Combine operators for error handling.

Mapping errors using mapError

To map an error to the expected error type we can use the mapError operator. In the following example, we have a passthrough subject which expects a URL output and a RequestError error type.

Once we start mapping this stream into a URLSessionDataTaskPublisher we immediately get an error pointing out the error type mismatch.

Error handling in Swift Combine using mapError

In this case, the solution is as simple as using the mapError operator which will wrap the URLError into a RequestError using the session error case we defined earlier.

Using the retry operator

In the above example, we’ve used a URLSessionDataTaskPublisher . You might want to use the retry operator before actually accepting an error when working with data requests. It takes the number of retries to take before letting the stream actually fail.

Catching errors

If you want to catch errors early and ignore them after you can use the catch operator. This operator allows you to return a default value for if the request failed. Examples of this could be:

An empty array for search results

A default image placeholder if the image request failed

The latter is the one we will use in our example.

Using replaceError instead of catch

ReplaceError vs Catch: both operators seem very similar. The big difference is that the replaceError(:) operator is completely ignoring the error. As in the above example, we're doing nothing more than returning the placeholder notFoundImage in the case of an error.

We could simplify this by using the replace error operator to directly map any errors into our placeholder image:

When the assign(to:on:) operator is unavailable

A common example in which you’ll need to map errors is when you try to assign an outcoming value to a property of an object. You’ll try to use the autocompletion and you find out that the assign(to:on:) operator is unavailable. The following error will occur if you are forced to write the code either way:

Referencing instance method ‘assign(to:on:)’ on ‘Publisher’ requires the types ‘RequestError’ and ‘Never’ be equivalent