Apps are full of things that happen over time: a user types into a search field, a network request finishes, a timer fires, a setting changes. Combine, introduced in iOS 13, gives you one consistent way to handle all of them. Values flow from publishers through a chain of operators to subscribers, and you describe that pipeline declaratively instead of juggling callbacks.
Since Swift 5.5, async/await and AsyncSequence cover many of the same jobs, and since iOS 17 the Observation framework handles most SwiftUI data flow. Combine is still fully supported, though, and you’ll find it in a huge number of codebases, especially those built between 2019 and 2023.
Each article measures the behavior it describes and ends with an exercise. The examples use Swift 6 language mode and were compiled and run with the Swift 6.4 toolchain from Xcode 27.1 on macOS 26, so they also show where Combine, a framework compiled without strict concurrency checking, meets Swift 6’s actor isolation: which closures crash when a publisher delivers on a background thread, and how to write pipelines that do not. Time-based behavior is pinned down with a virtual-time scheduler defined in the time operators article, the networking examples run against the loopback server from the networking roadmap, the testing article is a Swift package run with swift test, and the SwiftUI comparisons count body evaluations in hosted views.
Article available Coming soon
1. The Core Ideas
Publishers, Subscribers, and Subscriptions
Every Combine pipeline, however many operators it chains, is built from three protocols. A publisher describes values that will arrive over time....
sink and assign
Almost every Combine pipeline in an app ends in one of two built-in subscribers. sink hands each value and the completion to...
Subjects: PassthroughSubject and CurrentValueSubject
Most of the world does not arrive as a publisher. A delegate method fires, a button’s action runs, a WebSocket handler receives...
@Published Properties
@Published is the most widely used piece of Combine, often by people who never import Combine on purpose. It is a property...
AnyCancellable and Memory Management
A Combine subscription is a small graph of objects: the source holds a subscription, the subscription holds the next operator, and so...
2. Operators
Transforming: map, flatMap, and scan
Three operators do most of the transforming in Combine, and each answers a different question. map asks what each value should become....
Filtering: filter, removeDuplicates, and compactMap
Filtering operators decide which values get through. filter keeps the values that satisfy a predicate. compactMap transforms each value and drops the...
Combining: combineLatest, zip, and merge
Real features depend on more than one stream. A sign-up button depends on a username, a password, and a terms checkbox. A...
Time: debounce, throttle, and delay
Time-based operators reshape when values arrive. debounce waits for a pause and then emits the latest value, which is how a search...
Sequence Operators: first, last, and collect
Most operators look at one value at a time. A second family looks at the stream as a whole, the way Sequence...
3. Real-World Combine
Networking with URLSession Publishers
URLSession.dataTaskPublisher(for:) turns an HTTP request into a publisher that emits one (data, response) pair and finishes, or fails with a URLError. It...
Timers
Foundation’s Timer gained a Combine face in iOS 13: Timer.publish(every:on:in:) returns a publisher that emits the current Date on each tick. It...
Key-Value Observing with Publishers
Key-Value Observing is the Objective-C runtime’s way of telling you that a property changed. It predates Combine by more than fifteen years,...
Notifications as Publishers
NotificationCenter is the oldest broadcast mechanism on Apple platforms, and the system still uses it for a long list of events: the...
4. Doing It Right
Error Handling: catch, retry, and replaceError
In Combine, an error is not an exception that unwinds a call stack; it is the last thing a stream ever says....
Schedulers: receive(on:) and subscribe(on:)
Combine does not pick threads. A value is delivered on whatever thread produced it, every operator runs on that thread, and the...
Managing Resources: share and Multicasting
Most publishers are cold: each subscriber gets its own subscription, and the subscription does the work. Two views bound to the same...
Debugging Pipelines with print and handleEvents
A Combine pipeline is hard to step through. The operators are generic structs inside a system framework, the work happens in subscriptions...
Testing Publishers
A pipeline is a promise about the future: when the user stops typing for 300 ms, send one request; when a newer...
5. Combine Today
Combine vs. async/await and AsyncSequence
Swift 5.5 gave the language its own model for values over time: async functions for one value, AsyncSequence for many, tasks for...
Bridging Publishers to async/await with .values
Most codebases do not move from Combine to async/await in one step. They grow a boundary: an async function that needs the...
Combine vs. the Observation Framework in SwiftUI
From iOS 13 to iOS 16, SwiftUI’s data flow was built on Combine. A view model conformed to ObservableObject, its state was...
Up next
RxSwift