Reputation: 36078
I'm struggling with handling errors in Combine and find it very counter-intuitive that catching or replacing an error completes and ends the publisher. For example, this ends the publisher even though I handled the exception:
Just([1, 2, 3, NaN, 5, 6])
.tryMap { _ in throw DummyError() }
.catch { _ in Just(4) } // or .replaceError(with: 4)
.sink { print($0) } // <-- ends at 4 and ignores 5, 6, and anything ever again
If the publisher was subscribed to some system change, the first error will make my app dead until the user restarts the app. I would like to create a catch
counterpart that does NOT end the publisher. I know flatMap
is used for that, but that creates a whole other dimension of complexity such as inverting inner/outer publishers, publishers getting duplicated infinitely, back pressure, etc.
Is there a way to do something like this:
Just([1, 2, 3, NaN, 5, 6])
.tryMap { _ in throw DummyError() }
.ignoreError { error in
log("Error occurred: \(error)")
}
.sink { print($0) } // 1, 2, 3, 5, 6
How can I encapsulate this intent in a simple custom chain command called ignoreError
and make my publishers live on?
Upvotes: 1
Views: 687
Reputation: 49590
An error completing the pipeline is part of the contract by which publishers and subscribers work. And this makes sense. If an upstream publisher throws an error and doesn't know how to recover on its own, it's basically done. It's not unlike a function throwing an error.
In your example, tryMap
throws an error, and although a downstream like replaceError
can transform the error for its downstream, it does not expect any more values from its upstream, so it too completes.
flatMap
shields the rest of the pipeline from the error-throwing publisher. In other words, the upstream of flatMap
doesn't throw an error, and flatMap
itself doesn't throw an error since the returned inner publisher (the pipeline with Just
/tryMap
/catch
) handles the error.
Just([1, 2, 3, 5, 6])
.flatMap {
Just($0)
.tryMap { _ in throw DummyError() }
.catch { _ in Just(4) }
}
.sink { print($0) }
Upvotes: 3