Sequence and IteratorProtocol

Introduction

Welcome to Lesson 5 of Advanced Swift. When it comes to learning new features, I’ve been using the microwave analogy that things just work because smart engineers have designed them. But, there are times you might have to create your own custom microwave. For example, you might want to create one for yourself during the winter because it’s hard to code with frozen fingers. Then, you’ve got know all. In this lesson, let’s find out the secret behind the Swift for-in loop and learn how to create our own custom types that have the magic.

Problem

What goes under the hood of a for-in loop.

Take For Granted

When you first learned Swift or other programming languages, you’ve probably used the feature below without giving a second thought. (I didn’t)

  1. let iOSSkills = ["Swift", "UIKIt", "RxSwift", "TDD"]
  2. for skill in iOSSkills {
  3. print(skill)
  4. }
  5. // Prints "Swift"
  6. // Prints "UIKIt"
  7. // Prints "RxSwift"
  8. // Prints ""TDD""

Let us convert a String value into CharacterView and loop each character.

  1. for character in ("Bob the Developer").characters {
  2. print(character)
  3. }
  4. // "B"
  5. // "o"
  6. // "b"
  7. // ...
  8. // "r"

Important: The Swift for-in loop exists since Array<String> and CharacterView both conforms to Sequence and IteratorProtocol.

Behind the Scene

First, the iOSSkill, whose type is Array<String> conforms to Sequence, has the required method, makeIterator. The method returns an object that conforms to IteratorProtocol. The returned object has a required method called, next() which returns the next element with an optional type.

  1. var skillIterator = iOSSkills.makeIterator()
  2. while let skill = skillIterator.next() {
  3. print(skill)
  4. }
  5. // Prints "Swift"
  6. // Prints "UIKIt"
  7. // Prints "RxSwift"
  8. // Prints ""TDD""

It also applies to Array<Int>.

  1. let numbers = [2, 3, 5, 7]
  2. var numbersIterator = numbers.makeIterator()
  3. numbersIterator.next()
  4. numbersIterator.next()
  5. numbersIterator.next() // Optional(5)
  6. numbersIterator.next()
  7. numbersIterator.next()
  8. numbersIterator.next()

Custom Type

Sequence Protocol

Let us create our own struct that conforms to Sequence. The struct is called, MyCountdown. The struct has the required method, makeIterator. The method returns an object that conforms to ProtocolIterator.

  1. struct MyCountdown: Sequence {
  2. let start: Int
  3. func makeIterator() -> CountdownIterator {
  4. return CountdownIterator(self)
  5. }
  6. }

IteratorProtocol

Let us create a struct, CountdownIterator that conforms to IteratorProtocol. The struct contains the required method, next() which returns an element.

  1. struct CountdownIterator: IteratorProtocol {
  2. let countdown: MyCountdown
  3. var times = 0
  4. init(_ countdown: MyCountdown) {
  5. self.countdown = countdown
  6. }
  7. mutating func next() -> Int? {
  8. let nextNumber = countdown.start - times
  9. guard nextNumber > 0
  10. else { return nil }
  11. times += 1
  12. return nextNumber
  13. }
  14. }

Testing

Let us find out if the makeIterator and next methods work.

  1. var threeTwoOne = MyCountdown(start: 3)
  2. var iterator = threeTwoOne.makeIterator()
  3. iterator.next() // 3
  4. iterator.next() // 2
  5. iterator.next() // 1

Magic

The MyCountdown. is eligible for the Swift for-in loop.

  1. for count in threeTwoOne {
  2. print("\(count)...")
  3. }

Source Code

8005_sequence_iterator.playground

Resources

Swift Standard Library Functions

Swift Change Log

Swiftdoc.org

References

API Reference - IteratorProtocol

Ray Wenderlich - Building Custom Collection Swift

Protocols in Swift by Chris Eidhof

How To Conform to the Sequence Protocol - Natasha The Robot

Conclusion

First, you’ve learned the hidden implementation by the Swift for-in loop. To make types eligible for the magic, it has to conform to the Sequence protocol and has the required method that returns an object that conforms to IteratorProtocol. The iterator object also has the required method of next() to get the next element. The word sequence sounds as if types must look like a dictionary, array, or set. It doesn’t have to. Again, the purpose of this lesson was not introduce you to various protocols. It was to let you know how native types conform to pre-written protocols and has added features like a for-in loop. If you wish to study, I recommend you to take a look at the references and read the documentation.

In the following lesson, you will learn how to execute swift files using Terminal and how Xcode sends error messages, a.k.a, the red marks.

Note: Learn Swift with Bob is available on Udemy. If you wish to receive a discount link, you may sign up here.