A pipeline goes silent after one network error — why, and how do catch/retry/replaceError keep it alive?
This search pipeline stops emitting after the first failed request and never recovers, even once the network is back.
$query
.flatMap { query in
URLSession.shared.dataTaskPublisher(for: url(query))
.map(\.data)
}
.receive(on: DispatchQueue.main)
.sink(
receiveCompletion: { _ in },
receiveValue: { self.data = $0 }
)
.store(in: &cancellables)
Explain why one error kills the whole stream and fix it so a failure does not stop future queries.
A .failure completion is terminal — it tears down the subscription, so nothing flows afterward. Put retry, catch, or replaceError on the inner publisher inside flatMap so the error is absorbed there and the outer $query stream keeps emitting.
- ✗Believing a
.failurecompletion is recoverable without an operator - ✗Placing
catch/retryon the outer chain instead of the inner publisher - ✗Assuming an empty
receiveCompletionclosure keeps the stream alive
- →Why does
catchon the inner publisher protect the outer stream? - →When would
retryalone be worse thanretrypluscatch?
A failure completion is terminal — it tears down the whole subscription. Keep the error inside the inner publisher so the outer $query stream survives:
$query
.flatMap { query in
URLSession.shared.dataTaskPublisher(for: url(query))
.map(\.data)
.retry(2)
.catch { _ in Just(Data()) } // or replaceError(with:)
}
.receive(on: DispatchQueue.main)
.sink { self.data = $0 }
.store(in: &cancellables)
catch/retry/replaceError on the inner publisher convert or absorb the failure before it reaches the outer subscription, so the outer stream never receives a .failure completion and keeps delivering later queries.