UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

How to loop over an AsyncSequence using for await

Paul Hudson    @twostraws   

Updated for Xcode 15

You can loop over an AsyncSequence using Swift’s regular loop types, for, while, and repeat, but whenever you read a value from the async sequence you must use either await or try await depending on whether it can throw errors or not.

As an example, URL has a built-in lines property that generates an async sequence of all the lines directly from a URL. This does a lot of work internally: making the network request, fetching part of the data, converting it into a string, sending it back for us to use, then repeating fetch, convert, and send again and again until the server stops sending back data.

All that functionality boils down to just a handful of lines of code:

func fetchUsers() async throws {
    let url = URL(string: "https://hws.dev/users.csv")!

    for try await line in url.lines {
        print("Received user: \(line)")
    }
}

try? await fetchUsers()

Download this as an Xcode project

Notice how we must use try along with await, because fetching data from the network might throw errors.

Using lines returns a specialized type called AsyncLineSequence, which returns individual lines from the download as strings. Because our source happens to be a comma-separated values file (CSV), we can write code to read the values from each line easily enough:

func printUsers() async throws {
    let url = URL(string: "https://hws.dev/users.csv")!

    for try await line in url.lines {
        let parts = line.split(separator: ",")
        guard parts.count == 4 else { continue }

        guard let id = Int(parts[0]) else { continue }
        let firstName = parts[1]
        let lastName = parts[2]
        let country = parts[3]

        print("Found user #\(id): \(firstName) \(lastName) from \(country)")
    }
}

try? await printUsers()

Download this as an Xcode project

Just like a regular sequence, using an async sequence in this way effectively generates an iterator then calls next() on it repeatedly until it returns nil, at which point the loop finishes.

If you want more control over how the sequence is read, you can of course create your own iterator then call next() whenever you want and as often as you want. Again, it will send back nil when the sequence is empty, at which point you should stop calling it.

For example, we could read the first user from our CSV and treat them specially, then read the next four users and do something specific for them, then finally reduce all the remainder down into an array of other users:

func printUsers() async throws {
    let url = URL(string: "https://hws.dev/users.csv")!

    var iterator = url.lines.makeAsyncIterator()

    if let line = try await iterator.next() {
        print("The first user is \(line)")
    }

    for i in 2...5 {
        if let line = try await iterator.next() {
            print("User #\(i): \(line)")
        }
    }

    var remainingResults = [String]()

    while let result = try await iterator.next() {
        remainingResults.append(result)
    }

    print("There were \(remainingResults.count) other users.")
}

try? await printUsers()

Download this as an Xcode project

BUILD THE ULTIMATE PORTFOLIO APP Most Swift tutorials help you solve one specific problem, but in my Ultimate Portfolio App series I show you how to get all the best practices into a single app: architecture, testing, performance, accessibility, localization, project organization, and so much more, all while building a SwiftUI app that works on iOS, macOS and watchOS.

Get it on Hacking with Swift+

Sponsor Hacking with Swift and reach the world's largest Swift community!

Similar solutions…

BUY OUR BOOKS
Buy Pro Swift Buy Pro SwiftUI Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Coding Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Advanced iOS Volume Three Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

Was this page useful? Let us know!

Average rating: 3.8/5

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.