Operators
Composing streams, and where the thread goes
Operators are publishers that wrap other publishers. map returns a Publishers.Map, which holds
its upstream and transforms each value as it passes. Nothing runs until something subscribes.
let results = searchTerms
.removeDuplicates()
.flatMap { term in
api.search(term)
.catch { _ in Just([]) }
}
.receive(on: DispatchQueue.main)
Three things worth knowing about this chain:
flatMapkeeps every inner publisher alive until it completes. For search-as-you-type that means overlapping requests and out-of-order results;switchToLatestcancels the previous one.catchends the stream it recovers. Once an error passes through, that upstream is finished — which is why the recovery is placed insideflatMap, on the inner publisher, not outside it.receive(on:)moves delivery, not work. Whateverapi.searchdoes happens where it already happened; only the values after this point arrive on main.
Why these chapters are grouped this way
Combine has around a hundred operators and an alphabetical tour of them would be useless. They are grouped here by the question they answer, because in practice you know what you want and need the name.
Transforming — one stream in, one stream out, values changed on the way. map, compactMap,
scan, flatMap, switchToLatest, and the difference between the last two, which is the most
consequential choice in this book.
Combining — several streams in, one out. merge, combineLatest, zip, and the
first-value-only operators. Choosing wrongly here produces a screen that updates too often or one
that never updates at all, and the three behave very differently the moment one source is slower
than the others.
Timing — debounce, throttle, delay, timeout, collect(.byTime:), and measureInterval.
Everything here needs a scheduler, and the choice of scheduler changes the behaviour.
Failure — catch, retry, replaceError, mapError, setFailureType, and the discipline of
deciding where in a chain an error is allowed to be fatal.
The rule underneath all of them
An error terminates the stream it reaches. Not the value — the stream. Once a failure passes a point in the chain, everything downstream of it receives a completion and the subscription is over. No later value can arrive, because there is nothing left to arrive on.
This is the single most important sentence about Combine operators, and it drives the placement of
almost every error-handling operator you will write. A catch at the end of a pipeline recovers
once and then the pipeline is dead; the same catch inside a flatMap recovers the inner publisher
and leaves the outer stream alive to handle the next value.
Almost every “it worked the first time and then stopped” bug in Combine is that distinction.