Introduction
What Combine is for, and what has replaced most of it
Combine is Apple’s framework for values that arrive over time: a network response, a text field’s
contents, a notification, the tick of a timer. Instead of a callback per source, every source becomes
a Publisher, and publishers compose.
let cancellable = NotificationCenter.default
.publisher(for: UITextField.textDidChangeNotification)
.compactMap { ($0.object as? UITextField)?.text }
.debounce(for: .milliseconds(300), scheduler: RunLoop.main)
.removeDuplicates()
.sink { query in search(query) }
Four operators replace a debounce timer, a “did it change?” check and a delegate.
Warning
Much of what Combine was written for is now in the language. async/await covers one-shot
work better than Future, and AsyncSequence covers streams with less machinery. Reach for
Combine when you genuinely need its operator vocabulary — merging, combining latest values,
backpressure — not out of habit.
An honest position on a framework in decline
This book is not going to pretend Combine is the future. Apple has not given it a meaningful update
in years, @Observable removed the reason most apps had it — ObservableObject and @Published —
and for new work, structured concurrency is the better default in most situations.
So why a book at all? Two reasons, and both are practical.
It is in the codebase you inherited. Combine went into a great deal of production iOS code between 2019 and 2023, and that code still ships. Reading it, debugging it and migrating it in pieces requires understanding it properly, and “just rewrite it in async/await” is not a plan when the pipeline is four hundred lines and load-bearing.
Some problems are still awkward without it. Combining the latest values from three independent sources, debouncing, throttling, retrying with backoff, sharing one in-flight request among several subscribers — these have async/await answers, but the answers are hand-written, and the Combine versions are one operator each.
The honest framing: Combine is a tool with a narrowing set of good uses, and knowing exactly where that set ends is more valuable than either enthusiasm or dismissal.
What this book covers
Publishers and subscribers is the contract — the protocols, the four-step handshake underneath every stream, subjects for bridging imperative code, and demand, which is the concept most Combine users never learn and the one that explains the framework’s odd corners.
Operators is the vocabulary, in four chapters grouped by what they do rather than alphabetically: transforming, combining, timing, and failure handling.
Schedulers and threads is where work happens and where values are delivered, which are different questions with different answers — and the source of most Combine threading bugs.
Cancellation and lifetime is AnyCancellable, the retain cycles it invites, and why a stream can
stop silently with nothing logged.
Testing a pipeline covers making time controllable so a debounce test does not take three seconds.
Combine and async/await is the interoperation chapter and the migration one — .values,
Future, bridging in both directions, and how to decide which parts of an existing pipeline are
worth moving.
What it assumes
Swift, including generics and closures, and enough concurrency to know what a Task is. It does not
assume you have used a reactive framework before — no RxSwift knowledge is needed, and where a
Combine name differs from the Rx one that convention would suggest, the book says so.