Consuming AsyncSequence with for-await

You know how to await a single value. But what about a stream of values that arrive over time?
Follow along with the code: iOS-Practice on GitHub
The for-await Loop
AsyncSequence is like Sequence, but each element is delivered asynchronously. You consume it with for await:
for await number in numberStream {
print("Got: \(number)")
}
print("Stream finished")
The loop suspends at each iteration, waiting for the next value. When the sequence ends, the loop exits normally.
How It Works
struct NumberStream: AsyncSequence {
typealias Element = Int
let count: Int
let delay: TimeInterval
struct AsyncIterator: AsyncIteratorProtocol {
var current = 0
let count: Int
let delay: TimeInterval
mutating func next() async throws -> Int? {
guard current < count else { return nil }
try await Task.sleep(nanoseconds: UInt64(delay * 1_000_000_000))
current += 1
return current
}
}
func makeAsyncIterator() -> AsyncIterator {
AsyncIterator(count: count, delay: delay)
}
}
The key method is next():
- Returns the next value when available
- Returns
nilto signal the end - Can throw errors
Cancellation is Automatic
One of the best features: task cancellation works automatically.
let task = Task {
for await value in someStream {
process(value)
}
}
// Later...
task.cancel() // Loop exits cleanly
When the task is cancelled, the for await loop stops iterating. No manual checking required.
Breaking Early
You can exit a for await loop early just like a regular loop:
for await message in chatMessages {
if message.contains("goodbye") {
break // Stop consuming the stream
}
display(message)
}
Error Handling
For throwing sequences, wrap in do-catch:
do {
for try await data in networkStream {
process(data)
}
} catch {
print("Stream failed: \(error)")
}
The Mental Model
Think of for await as:
- Pull-based: You request the next value
- Suspending: Waits without blocking a thread
- Cancellation-aware: Respects task cancellation
- Sequential: One value at a time, in order
Interview Tip
When discussing AsyncSequence, mention that it's the async equivalent of Sequence—same protocol pattern (makeAsyncIterator, next()), but with suspension points. This shows you understand the design philosophy, not just the syntax.