Iterator in Swift


Introduction to Iterators in Swift

An iterator in Swift is an object that allows traversing through a sequence of elements one at a time. Iterators are used to access collection elements without exposing their internal representation.

Swift provides built-in IteratorProtocol, which enables custom iteration over collections like arrays, dictionaries, and custom data structures.

Key Features of Iterators in Swift:

  • Sequential Access – Access elements one by one.
  • Custom Iterators – Define custom iteration logic.
  • Memory Efficiency – Process elements lazily.
  • Supports Generators – Works with sequences and collections.

1. Built-in Iterators in Swift

Swift's collections (Array, Set, Dictionary) come with default iterators.

Example 1: Iterating Over an Array

let numbers = [10, 20, 30, 40]
var iterator = numbers.makeIterator()

while let number = iterator.next() {
     print("Next number: \(number)")
}

Output

Next number: 10
Next number: 20
Next number: 30
Next number: 40

.makeIterator() creates an iterator.
.next() fetches the next element.
Returns nil when all elements are exhausted.



Example 2: Iterating Over a Dictionary

let cities = ["NY": "New York", "CA": "California"]
var cityIterator = cities.makeIterator()

while let city = cityIterator.next() {
     print("Key: \(city.key), Value: \(city.value)")
}

Output

Key: NY, Value: New York
Key: CA, Value: California

Iterators work on key-value pairs.

2. Creating a Custom Iterator in Swift

To create a custom iterator, we implement IteratorProtocol and define a next() method.


Example 3: Creating a Custom Number Iterator

struct NumberIterator: IteratorProtocol {
     var current = 1

     mutating func next() -> Int? {
         if current <= 5 {
             let value = current
             current += 1
             return value
         }
         return nil
     }
}

// Using the custom iterator
var numIterator = NumberIterator()

while let number = numIterator.next() {
     print("Custom Iterator: \(number)")
}

Output

Custom Iterator: 1
Custom Iterator: 2
Custom Iterator: 3
Custom Iterator: 4
Custom Iterator: 5

next() returns one element at a time.
Stops when the limit is reached (nil).

3. Creating a Custom Sequence with an Iterator

A sequence is a collection that provides an iterator.

Example 4: Implementing a Custom Sequence

struct CountSequence: Sequence {
     var start: Int
     var end: Int

     func makeIterator() -> CountIterator {
         return CountIterator(current: start, end: end)
     }
}

// Custom Iterator
struct CountIterator: IteratorProtocol {
     var current: Int
     let end: Int

     mutating func next() -> Int? {
         guard current <= end else { return nil }
         defer { current += 1 }
         return current
     }
}

// Using the custom sequence
let count = CountSequence(start: 1, end: 5)

for num in count {
     print("Sequence: \(num)")
}

Output

Sequence: 1
Sequence: 2
Sequence: 3
Sequence: 4
Sequence: 5

makeIterator() returns an iterator.
for-in loop automatically calls .next().

4. Lazy Iterators for Performance Optimization

Lazy iterators process elements only when needed, improving efficiency.

Example 5: Using Lazy Iterators for Performance

let numbers = [1, 2, 3, 4, 5].lazy.map { $0 * 2 }

for num in numbers {
     print("Lazy Value: \(num)")
}

Output

Lazy Value: 2
Lazy Value: 4
Lazy Value: 6
Lazy Value: 8
Lazy Value: 10

.lazy processes only required elements.

5. Real-World Use Cases of Iterators

Use Case Iterator Type
Traversing Arrays & Dictionaries makeIterator()
Building Custom Data Streams Custom IteratorProtocol
Efficient Processing of Large Data Lazy Sequences
Handling Infinite Data Streams Infinite Iterators

6. Infinite Iterators in Swift

An infinite iterator generates an endless sequence.

Example 6: Creating an Infinite Number Generator

struct InfiniteCounter: IteratorProtocol {
     var current = 1

     mutating func next() -> Int? {
         defer { current += 1 }
         return current
     }
}

// Using Infinite Iterator
var counter = InfiniteCounter()

for _ in 1...5 {
     print("Infinite Count: \(counter.next()!)")
}

Output

Infinite Count: 1
Infinite Count: 2
Infinite Count: 3
Infinite Count: 4
Infinite Count: 5

Never-ending iteration unless manually stopped.

7. Using Iterators in Functional Programming


Example 7: Filtering Elements with Iterators

let numbers = [1, 2, 3, 4, 5]
let filteredNumbers = numbers.lazy.filter { $0 % 2 == 0 }

for num in filteredNumbers {
     print("Filtered Number: \(num)")
}

Output

Filtered Number: 2
Filtered Number: 4

lazy.filter {} optimizes performance.

Conclusion

Iterators in Swift simplify sequential data access and enable custom traversal mechanisms. They improve efficiency, readability, and flexibility in handling collections and streams.