Iterator Design Pattern in Swift


Greetings, traveler!

We continue to analyze Behavioral Design Patterns. The Iterator is next on the list. In fact, we all use them all the time because the means of implementing them are already built into most modern programming languages, such as Swift.

However, understanding this pattern has not harmed anyone yet. Therefore, let’s look at what it is and how it works.

Example of usage

The Iterator Design Pattern is a way to access a container object’s elements sequentially. This includes types of containers such as arrays and sets.

Now, let’s try our hand at implementing the Iterator Design Pattern in Swift. To do this, we’ll need to ensure our variable adheres to the Sequence protocol. This protocol, in turn, requires our variable to have an associated type called Iterator, which must also implement the IteratorProtocol. We’ll need to include a method called makeIterator() that returns an iterator for our specific type. The IteratorProtocol protocol only contains one method – next(), which returns the next element in the sequence.

Let’s take a look at how it looks in the code.

struct AudioCassette {
    let title: String
}

struct AudioCassettes {
    let audioCassettes: [AudioCassette]
}

struct AudioCassettesIterator: IteratorProtocol {

    private let audioCassettes: [AudioCassette]
    private var current: Int = .zero
    
    init(audioCassettes: [AudioCassette]) {
        self.audioCassettes = audioCassettes
    }

    mutating func next() -> AudioCassette? {
        defer {
            current += 1
        }
        return audioCassettes.count > current ? audioCassettes[current] : nil
    }
}

extension AudioCassettesIterator: Sequence {
    func makeIterator() -> AudioCassettesIterator {
        AudioCassettesIterator(audioCassettes: audioCassettes)
    }
}

Now, we can use it:

let audioCassettes = AudioCassettes(audioCassettes: [
    .init(title: "80s best disco music"),
    .init(title: "90s best electronic music")
])

for audioCassette in audioCassettes {
    dump(audioCassette)
}

Conclusion

Well, that’s all for now. I hope this was clear and at least somewhat interesting for you. In the meantime, let’s move on to the next article, which will discuss the Mediator Design Pattern.