AsyncIteratorProtocol

A type that asynchronously supplies the values of a sequence one at a time.

iOS
13.0+
macOS
10.15+
tvOS
13.0+
watchOS
6.0+
protocol AsyncIteratorProtocol<Element, Failure>
Browse conforming types

The AsyncIteratorProtocol defines the type returned by the makeAsyncIterator() method of the AsyncSequence protocol. In short, the iterator is what produces the asynchronous sequence’s values. The protocol defines a single asynchronous method, next(), which either produces the next element of the sequence, or returns nil to signal the end of the sequence.

To implement your own AsyncSequence, implement a wrapped type that conforms to AsyncIteratorProtocol. The following example shows a Counter type that uses an inner iterator to monotonically generate Int values until reaching a howHigh value. While this example isn’t itself asynchronous, it shows the shape of a custom sequence and iterator, and how to use it as if it were asynchronous:

struct Counter: AsyncSequence {
    typealias Element = Int
    let howHigh: Int

    struct AsyncIterator: AsyncIteratorProtocol {
        let howHigh: Int
        var current = 1

        mutating func next() async -> Int? {
            // A genuinely asynchronous implementation uses the `Task`
            // API to check for cancellation here and return early.
            guard current <= howHigh else {
                return nil
            }

            let result = current
            current += 1
            return result
        }
    }

    func makeAsyncIterator() -> AsyncIterator {
        return AsyncIterator(howHigh: howHigh)
    }
}

At the call site, this looks like:

for await number in Counter(howHigh: 10) {
  print(number, terminator: " ")
}
// Prints "1 2 3 4 5 6 7 8 9 10 "

End of Iteration

The iterator returns nil to indicate the end of the sequence. After returning nil (or throwing an error) from next(), the iterator enters a terminal state, and all future calls to next() must return nil.

Cancellation

Types conforming to AsyncIteratorProtocol should use the cancellation primitives provided by Swift’s Task API. The iterator can choose how to handle and respond to cancellation, including:

  • Checking the isCancelled value of the current Task inside next() and returning nil to terminate the sequence.

  • Calling checkCancellation() on the Task, which throws a CancellationError.

  • Implementing next() with a withTaskCancellationHandler(handler:operation:) invocation to immediately react to cancellation.

If the iterator needs to clean up on cancellation, it can do so after checking for cancellation as described above, or in deinit if it’s a reference type.