WWDC23 SALE: Save 50% on all my Swift books and bundles! >>

Async sequences

Available from Swift 5.5

Paul Hudson      @twostraws

SE-0298 introduced the ability to loop over asynchronous sequences of values using a new AsyncSequence protocol. This is helpful for places when you want to process values in a sequence as they become available rather than precomputing them all at once – perhaps because they take time to calculate, or because they aren’t available yet.

Using AsyncSequence is almost identical to using Sequence, with the exception that your types should conform to AsyncSequence and AsyncIterator, and your next() method should be marked async. When it comes time for your sequence to end, make sure you send back nil from next(), just as with Sequence.

For example, we could make a DoubleGenerator sequence that starts from 1 and doubles its number every time it’s called:

struct DoubleGenerator: AsyncSequence {
    typealias Element = Int

    struct AsyncIterator: AsyncIteratorProtocol {
        var current = 1

        mutating func next() async -> Int? {
            defer { current &*= 2 }

            if current < 0 {
                return nil
            } else {
                return current
            }
        }
    }

    func makeAsyncIterator() -> AsyncIterator {
        AsyncIterator()
    }
}

Tip: If you just remove “async” from everywhere it appears in that code, you have a valid Sequence doing exactly the same thing – that’s how similar these two are.

Once you have your asynchronous sequence, you can loop over its values by using for await in an async context, like this:

func printAllDoubles() async {
    for await number in DoubleGenerator() {
        print(number)
    }
}

The AsyncSequence protocol also provides default implementations of a variety of common methods, such as map(), compactMap(), allSatisfy(), and more. For example, we could check whether our generator outputs a specific number like this:

func containsExactNumber() async {
    let doubles = DoubleGenerator()
    let match = await doubles.contains(16_777_216)
    print(match)
}

Again, you need to be in an async context to use this.

Save 50% in my WWDC23 sale.

SAVE 50% To celebrate WWDC23, all our books and bundles are half price, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.

Save 50% on all our books and bundles!

Other changes in Swift 5.5…

Download all Swift 5.5 changes as a playground Link to Swift 5.5 changes

Browse changes in all Swift versions

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.