Combine vs. async/await: Reactive Patterns in Swift

iOS Development — Architecture & Data

Chapter 9 · Combine vs. async/await: Reactive Patterns in Swift

Every real, ongoing stream this course has touched so far — a single fetched quote, one refresh call — has been a genuine one-shot operation. Some real data genuinely arrives as a continuous stream of many values over time. This chapter covers both real tools for that: Combine, and async/await's own newer answer, AsyncSequence.

Combine: Real, Established, Genuinely Older

Introduced in iOS 13, 2019, Combine is Apple's own real, functional-reactive framework — a Publisher emits a real stream of values over time; a Subscriber receives them, often through real operators like map, filter, and the terminal sink.

import Combine [1, 2, 3, 4, 5].publisher .filter { $0 > 2 } .map { $0 * 2 } .sink { print($0) } // prints 6, 8, 10

A Real, Honest Positioning

Apple's Own Real, Current Guidance Has Shifted
Since async/await and AsyncSequence arrived in Swift 5.5, 2021, Apple's own real emphasis for new code has genuinely moved toward them. Combine remains real, supported, and still genuinely useful for complex multi-stream reactive logic and for existing, already-Combine-based codebases — but it's no longer the automatic real default for new SwiftUI apps the way it once was.

AsyncSequence: The Real async/await Equivalent

Where a Combine Publisher emits values a Subscriber reacts to, an AsyncSequence is a real sequence a caller iterates directly with for await — conceptually the same real job, expressed in the same language this course has used throughout.

for await notification in NotificationCenter.default.notifications(named: .NSSystemClockDidChange) { print("System clock changed") }
A Real, Concrete Comparison
NotificationCenter.default.notifications(named:) is a real, current, async/await-native alternative to what Combine's own NotificationCenter.Publisher used to be needed for — and it carries a genuine, verified structural advantage: observation automatically stops the moment the surrounding for await loop exits or its own Task is cancelled, with no manual subscription cleanup required at all.

Building a Real Custom Stream: AsyncStream

For a genuinely custom, multi-value stream — not one Apple already provides an AsyncSequence for — AsyncStream is the real, direct async/await equivalent of manually building a Combine Publisher:

func countdown(from start: Int) -> AsyncStream<Int> { AsyncStream { continuation in Task { for value in stride(from: start, through: 0, by: -1) { continuation.yield(value) try? await Task.sleep(for: .seconds(1)) } continuation.finish() } } } for await value in countdown(from: 3) { print(value) // 3, 2, 1, 0 — one per real second }
Combineasync/await
Real IntroductioniOS 13, 2019Swift 5.5, iOS 15, 2021
Multi-Value StreamPublisherAsyncSequence/AsyncStream
Consuming.sink { }, operator chainsfor await, the same syntax as every other async call in this course
Apple's Real Current EmphasisExisting codebases, complex multi-stream operatorsNew code, by real, current default
A Real, Practical Rule for New Code
Reaching for AsyncSequence/AsyncStream keeps a codebase speaking one real, consistent async language throughout — the exact same async/await/Task vocabulary this course has used since Chapter 2, rather than mixing in a second, genuinely different real paradigm just for streams of multiple values.

Hands-On Exercises

Exercise 1

Write a real function timer(every interval: Duration, count: Int) -> AsyncStream<Int> that yields 1 through count, one value every real interval, then finishes — and a real for await loop consuming it.

📄 View solution
Exercise 2

Explain, in your own words, why NotificationCenter.default.notifications(named:)'s own real automatic-stop-on-cancellation behavior is a genuine, concrete advantage over a manually managed Combine subscription, which requires explicitly storing and later cancelling an AnyCancellable.

📄 View solution
Exercise 3

Explain, in your own words, why choosing AsyncSequence/AsyncStream over Combine for new code in a project already using async/await throughout (like TaskFlow) is a genuine, practical consistency win, not just a matter of personal preference.

📄 View solution

Chapter 9 Quick Reference

  • Combine (iOS 13, 2019) — real, established functional-reactive framework: Publisher, Subscriber, operators like map/filter/sink
  • Apple's own real, current guidance has genuinely shifted toward async/await for new code since Swift 5.5 (2021) — Combine remains real and supported, not deprecated
  • AsyncSequence is the real async/await equivalent of a Combine Publisher, consumed with for await
  • NotificationCenter.default.notifications(named:) — a real, current, verified example: automatically stops observing on scope exit or cancellation, no manual cleanup
  • AsyncStream — the real, direct way to build a genuinely custom multi-value async stream