Sequence — beyond primitive iterations in Swift

One protocol to iterate them all

Mikael Konradsson
Swift Programming

--

Iterations

One fundamental brick in programming is iterations. All modern programming languages have them and swift is no exception. Just as it’s predecessor Swift offers the many varieties from C/C++ iterations, like do/while/for in different combinations but also offers a more modern approach with for/in (foreach in some other languages) for fast enumeration. Swift also offers a more modern syntax for for/in with Range Operators:

Half Open/Closed Range Operator

//Half Open Range
for x in 0..<10 { //prints 0 to 9}
//Full Range
for x in 0...10 { //prints 0 to 10}

But what if I want to go beyond primitive types and use more complex types?

Sequence Protocol

In Swift, Strings, Arrays, Dictionaries implements the Sequence-protocol, but in Objective-C all the counterpart classes (except NSString) inherited from an abstract base class called NSEnumerator. Swift doesn’t have support for abstract classes, and to be honest a Protocol is a cleaner approach.

I’ve had a hard time finding documentation of the Sequence-protocol in Swift. But I could find some by importing Swift to Playground/project and open Swift framework file.

import Swift //cmd-click on ‘Swift’

Below you can see the declaration of the Sequence-protocol. It only has one function that needs to be implemented, the generate()-method.

protocol Sequence {
typealias GeneratorType : Generator
func
generate() -> GeneratorType
}

What generate() does is that it returns a Type that implements Generator Protocol and call next() (which will return and remove first object from collection). This is repeated until it reaches nil. See example below.

var generator: Generator = sequence.generate() 
while let i = generator.next() {}

Let’s build a new type that supports iteration

Everybody love lists, and therefore let us build a stub for a TodoItem DataLayer. Our model only has two properties, name and priority. They are both declared as non-optional because we initialize them both during instance initialization.

Model, TodoItem

class TodoItem {
var name: String
var priority: Int

init
(name: String, priority: Int) {
self.name = name
self.priority = priority
}
}

Version 1 (TodoItemRepository)

To store and receive our Model-data we create a basic Repository/Mediator.

class TodoItemRepository {
var items: [TodoItem] = []
func addItem(item: TodoItem) {
items += item
}
}

We can iterate our TodoItems through the access of the public Item list.

for item in todoItemRepository.items {}

But wouldn’t it be more smooth if we could iterate directly using todoItemRepository instead? Like the example below!

for item in todoItemRepository {}

Let’s implement and conform to Sequence protocol

version 2

class TodoItemRepository : Sequence {
var items: [TodoItem] = []
func addItem(item: TodoItem) {
items += item
}
func generate() -> GeneratorOf<TodoItem> {
var i = 0
return GeneratorOf<TodoItem> {
return i >= self.items.count ? .None : self.items[i++]
}
}
}

The first four lines are identical to our first version, except that we now want to implement Sequence protocol. Therefore we must add the generate() method. The implementation of generate() might need some explanation. If there are no items, we return .None (which is an Optional with nil-value) otherwise we increase index and return item at current index.

We now have a working version of TodoItemRepository. We can however do this a little bit different and it would possibly make things a little bit clearer and easier to reuse.

Lets create a separate generic Generator that can be reused for future usage.

GenericGenerator

struct GenericGenerator<T>: Generator {
var items: [T]
mutating func next() -> T? {
return items.isEmpty ? .None : items.removeAtIndex(0)
}
}

As mentioned before, above we can clearly see that next() will return an optional type T?.

Version 3 with generic generator.

class TodoItemRepository : Sequence {
var items: [TodoItem] = []
func addItem(item: TodoItem) {
items += item
}
func generate() -> GenericGenerator<TodoItem> {
return GenericGenerator(items: items)
}
}

There you have it. It’s quite easy to create your own iterable Types.

Follow me on twitter: https://twitter.com/konrad1977

--

--