Protocol

IteratorProtocol

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

Overview

The IteratorProtocol protocol is tightly linked with the Sequence protocol. Sequences provide access to their elements by creating an iterator, which keeps track of its iteration process and returns one element at a time as it advances through the sequence.

Whenever you use a for-in loop with an array, set, or any other collection or sequence, you’re using that type’s iterator. Swift uses a sequence’s or collection’s iterator internally to enable the for-in loop language construct.

Using a sequence’s iterator directly gives you access to the same elements in the same order as iterating over that sequence using a for-in loop. For example, you might typically use a for-in loop to print each of the elements in an array.

let animals = ["Antelope", "Butterfly", "Camel", "Dolphin"]
for animal in animals {
    print(animal)
}
// Prints "Antelope"
// Prints "Butterfly"
// Prints "Camel"
// Prints "Dolphin"

Behind the scenes, Swift uses the animals array’s iterator to loop over the contents of the array.

var animalIterator = animals.makeIterator()
while let animal = animalIterator.next() {
    print(animal)
}
// Prints "Antelope"
// Prints "Butterfly"
// Prints "Camel"
// Prints "Dolphin"

The call to animals.makeIterator() returns an instance of the array’s iterator. Next, the while loop calls the iterator’s next() method repeatedly, binding each element that is returned to animal and exiting when the next() method returns nil.

Using Iterators Directly

You rarely need to use iterators directly, because a for-in loop is the more idiomatic approach to traversing a sequence in Swift. Some algorithms, however, may call for direct iterator use.

One example is the reduce1(_:) method. Similar to the reduce(_:_:) method defined in the standard library, which takes an initial value and a combining closure, reduce1(_:) uses the first element of the sequence as the initial value.

Here’s an implementation of the reduce1(_:) method. The sequence’s iterator is used directly to retrieve the initial value before looping over the rest of the sequence.

extension Sequence {
    func reduce1(
        _ nextPartialResult: (Element, Element) -> Element
    ) -> Element?
    {
        var i = makeIterator()
        guard var accumulated = i.next() else {
            return nil
        }

        while let element = i.next() {
            accumulated = nextPartialResult(accumulated, element)
        }
        return accumulated
    }
}

The reduce1(_:) method makes certain kinds of sequence operations simpler. Here’s how to find the longest string in a sequence, using the animals array introduced earlier as an example:

let longestAnimal = animals.reduce1 { current, element in
    if current.count > element.count {
        return current
    } else {
        return element
    }
}
print(longestAnimal)
// Prints "Butterfly"

Using Multiple Iterators

Whenever you use multiple iterators (or for-in loops) over a single sequence, be sure you know that the specific sequence supports repeated iteration, either because you know its concrete type or because the sequence is also constrained to the Collection protocol.

Obtain each separate iterator from separate calls to the sequence’s makeIterator() method rather than by copying. Copying an iterator is safe, but advancing one copy of an iterator by calling its next() method may invalidate other copies of that iterator. for-in loops are safe in this regard.

Adding IteratorProtocol Conformance to Your Type

Implementing an iterator that conforms to IteratorProtocol is simple. Declare a next() method that advances one step in the related sequence and returns the current element. When the sequence has been exhausted, the next() method returns nil.

For example, consider a custom Countdown sequence. You can initialize the Countdown sequence with a starting integer and then iterate over the count down to zero. The Countdown structure’s definition is short: It contains only the starting count and the makeIterator() method required by the Sequence protocol.

struct Countdown: Sequence {
    let start: Int

    func makeIterator() -> CountdownIterator {
        return CountdownIterator(self)
    }
}

The makeIterator() method returns another custom type, an iterator named CountdownIterator. The CountdownIterator type keeps track of both the Countdown sequence that it’s iterating and the number of times it has returned a value.

struct CountdownIterator: IteratorProtocol {
    let countdown: Countdown
    var times = 0

    init(_ countdown: Countdown) {
        self.countdown = countdown
    }

    mutating func next() -> Int? {
        let nextNumber = countdown.start - times
        guard nextNumber > 0
            else { return nil }

        times += 1
        return nextNumber
    }
}

Each time the next() method is called on a CountdownIterator instance, it calculates the new next value, checks to see whether it has reached zero, and then returns either the number, or nil if the iterator is finished returning elements of the sequence.

Creating and iterating over a Countdown sequence uses a CountdownIterator to handle the iteration.

let threeTwoOne = Countdown(start: 3)
for count in threeTwoOne {
    print("\(count)...")
}
// Prints "3..."
// Prints "2..."
// Prints "1..."

Topics

Associated Types

associatedtype Element

The type of element traversed by the iterator.

Required.

Instance Methods

func next()

Advances to the next element and returns it, or nil if no next element exists.

Required.