Dunfey · Hotel WWDC as data, est. 1983
Front desk everything
Years
Topics

2021 Swift

WWDC21 · 14 min · Swift

Meet AsyncSequence

Iterating over a sequence of values over time is now as easy as writing a “for” loop. Find out how the new AsyncSequence protocol enables a natural, simple syntax for iterating over anything from notifications to bytes being streamed from a server. We’ll also show you how to adapt existing code to provide asynchronous sequences of your own. To get the most out of this session, we recommend first watching “Meet async/await in Swift.”

Watch at developer.apple.com ↗

Transcript all transcripts

Code shown on screen · 14 snippets

QuakesTool swift · at 0:37 ↗
@main
struct QuakesTool {
    static func main() async throws {
        let endpointURL = URL(string: "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_month.csv")!

        // skip the header line and iterate each one 
        // to extract the magnitude, time, latitude and longitude
        for try await event in endpointURL.lines.dropFirst() {
            let values = event.split(separator: ",")
            let time = values[0]
            let latitude = values[1]
            let longitude = values[2]
            let magnitude = values[4]
            print("Magnitude \(magnitude) on \(time) at \(latitude) \(longitude)")
        }
    }
}
Iterating a Sequence swift · at 3:24 ↗
for quake in quakes {
    if quake.magnitude > 3 {
        displaySignificantEarthquake(quake)
    }
}
How the compiler handles iteration swift · at 3:52 ↗
var iterator = quakes.makeIterator()
while let quake = iterator.next() {
    if quake.magnitude > 3 {
        displaySignificantEarthquake(quake)
    }
}
How the compiler handles asynchronous iteration swift · at 4:11 ↗
var iterator = quakes.makeAsyncIterator()
while let quake = await iterator.next() {
    if quake.magnitude > 3 {
        displaySignificantEarthquake(quake)
    }
}
Iterating an AsyncSequence swift · at 4:28 ↗
for await quake in quakes {
    if quake.magnitude > 3 {
        displaySignificantEarthquake(quake)
    }
}
Terminating iteration early by breaking swift · at 5:36 ↗
for await quake in quakes {
    if quake.location == nil {
        break
    }
    if quake.magnitude > 3 {
        displaySignificantEarthquake(quake)
    }
}
Skipping values by continuing swift · at 5:51 ↗
for await quake in quakes {
    if quake.depth > 5 {
        continue
    }
    if quake.magnitude > 3 {
        displaySignificantEarthquake(quake)
    }
}
AsyncSequence might throw swift · at 6:05 ↗
do {
    for try await quake in quakeDownload {
        ...
    }
} catch {
    ...
}
Concurrently iterating inside an async task swift · at 7:15 ↗
let iteration1 = Task {
    for await quake in quakes {
        ...
    }
}

let iteration2 = Task {
    do {
        for try await quake in quakeDownload {
            ...
        }
    } catch {
        ...
    }
}

//... later on  
iteration1.cancel()
iteration2.cancel()
Reading bytes from a FileHandle swift · at 7:56 ↗
for try await line in FileHandle.standardInput.bytes.lines {
    ...
}
Reading lines from a URL swift · at 8:16 ↗
let url = URL(fileURLWithPath: "/tmp/somefile.txt")
for try await line in url.lines {
    ...
}
Reading bytes from a URLSession swift · at 8:49 ↗
let (bytes, response) = try await URLSession.shared.bytes(from: url)

guard let httpResponse = response as? HTTPURLResponse,
      httpResponse.statusCode == 200 /* OK */
else {
    throw MyNetworkingError.invalidServerResponse
}

for try await byte in bytes {
    ...
}
Notifications swift · at 9:12 ↗
let center = NotificationCenter.default
let notification = await center.notifications(named: .NSPersistentStoreRemoteChange).first {
    $0.userInfo[NSStoreUUIDKey] == storeUUID
}
Using an AsyncStream swift · at 11:10 ↗
class QuakeMonitor {
    var quakeHandler: (Quake) -> Void
    func startMonitoring()
    func stopMonitoring()
}

let quakes = AsyncStream(Quake.self) { continuation in
    let monitor = QuakeMonitor()
    monitor.quakeHandler = { quake in
        continuation.yield(quake)
    }
    continuation.onTermination = { @Sendable _ in
        monitor.stopMonitoring()
    }
    monitor.startMonitoring()
}

let significantQuakes = quakes.filter { quake in
    quake.magnitude > 3
}

for await quake in significantQuakes {
    ...
}

Resources