Combine Operators & Pipelines
Operators are how you shape a Combine stream. This lesson covers map , filter , debounce , combineLatest , and flatMap , plus delivering results safely with receive(on:) and assign(to:) .
Learn Combine Operators & Pipelines in our free Swift course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick…
Part of the free Swift course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
What You'll Learn in This Lesson
- • Transform values with map and screen them with filter
- • Tame rapid input with debounce and removeDuplicates
- • Merge sources with combineLatest
- • Chain async work with flatMap
- • Deliver on the right thread with receive(on:)
- • Pipe results into a property with assign(to:on:)
📊 Operator Cheat Sheet
Operator
Does
map
Transform each value
filter
Keep values passing a test
debounce
Emit after a quiet pause
combineLatest
Pair latest from each source
flatMap
Map to publisher, flatten
receive(on:)
Switch delivery queue
assign(to:on:)
Write into a property
1️⃣ map and filter
filter screens values; map transforms what survives. Chained together they read top to bottom, just like the data flows.
2️⃣ combineLatest
combineLatest joins two streams: whenever either emits, you get the newest value from each. It only begins once both have emitted at least once.
3️⃣ debounce, receive(on:), and assign
debounce waits for a pause before emitting — ideal for search-as-you-type. receive(on:) moves delivery to the main thread for UI safety, and assign(to:on:) writes straight into a property.
Now you try. Fill in the operator that assigns into a property.
Pro Tips
- 💡 Put receive(on: .main) just before UI work so the rest of the pipeline can run off the main thread.
- 💡 Pair debounce with removeDuplicates to skip redundant searches.
- 💡 Reach for flatMap when each value triggers its own async publisher (like a request).
- 💡 Keep pipelines short. If a chain grows long, extract a helper publisher.
Common Errors (and the fix)
- UI updates on a background thread — add receive(on: DispatchQueue.main) before your sink/assign.
- combineLatest never emits — one of the sources hasn't produced a first value yet.
- Too many searches firing — you forgot debounce on the input stream.
- flatMap type mismatch — its closure must return a Publisher, not a plain value (that's map ).
📋 Quick Reference
Goal
Code
Result
Transform
.map { $0 * 2 }
double each
Screen
.filter { $0 > 0 }
keep positives
Settle
.debounce(for:.seconds(1),…)
wait then emit
Join
a.combineLatest(b)
latest pair
Main thread
.receive(on:.main)
UI-safe
To property
.assign(to:\\.text, on:label)
set label.text
Frequently Asked Questions
Mini-Challenge: Live Word Counter
Build a pipeline that counts words as sentences arrive. Run it and compare with the comments.
🎉 Lesson Complete!
- ✅ map transforms, filter screens
- ✅ debounce waits for a pause; removeDuplicates skips repeats
- ✅ combineLatest pairs the latest from each source
- ✅ flatMap maps to a publisher and flattens it
- ✅ receive(on:) picks the delivery queue; assign(to:on:) writes a property
- ✅ Next lesson: Advanced Concurrency — async let, TaskGroup, and actors
Practice quiz
What does the map operator do in Combine?
- Transforms each emitted value
- Filters values
- Delays values
- Combines two publishers
Answer: Transforms each emitted value. map transforms every value the upstream publisher emits.
Which operator only lets through values that pass a test?
- map
- merge
- filter
- assign
Answer: filter. filter forwards only values for which the closure returns true.
What problem does debounce solve?
- Sorting values
- Waiting for a pause before emitting, e.g. for search text
- Combining errors
- Caching to disk
Answer: Waiting for a pause before emitting, e.g. for search text. debounce waits for a quiet interval, ideal for rapidly changing input like typing.
combineLatest emits when...
- Only the first publisher emits
- All publishers finish
- An error occurs
- Any input publisher emits, pairing the latest value from each
Answer: Any input publisher emits, pairing the latest value from each. combineLatest emits the most recent value from each source whenever any of them changes.
What does flatMap return for each value?
- A new publisher whose values are flattened into the stream
- A single value
- An array
- Nothing
Answer: A new publisher whose values are flattened into the stream. flatMap maps each value to a publisher and flattens the emissions into one stream.
Why use receive(on: DispatchQueue.main)?
- To speed up the network
- To deliver downstream values on the main thread for UI updates
- To filter values
- To cancel the subscription
Answer: To deliver downstream values on the main thread for UI updates. receive(on:) switches delivery to a chosen queue, e.g. main for UI work.
What does assign(to:on:) do?
- Transforms values
- Delays values
- Merges streams
- Writes each received value into a property via a key path
Answer: Writes each received value into a property via a key path. assign(to:on:) sets a value onto an object's property using a key path.
After debounce, which value is emitted?
- The first value
- The most recent value once the pause elapses
- A random value
- All buffered values
Answer: The most recent value once the pause elapses. debounce emits only the latest value after the specified quiet period.
Operators in Combine are chained to form a...
- Pipeline that transforms the stream step by step
- Database
- View hierarchy
- Single function
Answer: Pipeline that transforms the stream step by step. Chained operators build a pipeline that processes values as they flow.
removeDuplicates does what?
- Sorts the stream
- Adds delays
- Drops a value equal to the one immediately before it
- Removes all values
Answer: Drops a value equal to the one immediately before it. removeDuplicates suppresses consecutive equal values.
Continue this course
- Previous: Introduction to Combine
- Next: Advanced Concurrency