Swift is a truly powerful language. But a powerful language is nothing if it doesn’t have a powerful standard library. In this post we will get started with the Swift Standard library (which is small compared to the standard library of other languages, but by no means does that mean it isn’t powerful). The Swift Standard library goes hand in hand with Apple’s new programming paradigm – Protocol-Oriented Programming. If you are acquainted with Protocol-Oriented Programming, you know how different it is compared to the so-known OOP. Swift’s Standard Library adopts Protocol-Oriented Programming really well (and that’s to be expected, since they created this paradigm!)

To be more specific, we will explore three protocols from Swift’s Standard Library here – GeneratorType, SequenceType and ArrayLiteralConvertible. We are going to build a Shopping List that behaves like an array – You will be able to create a Shopping List passing it an array of Shopping Items, and you will be able to fast-iterate (using for-in) over this list to get its elements as if it were a normal array. But this shopping list also has its own methods, so we are not just creating a new array with a fancy name.

A Simple Shopping List

If you wanted to create a shopping list of things, you would probably start by creating a class or an struct (in this post, we will use an struct to also embrace the power of value types in Swift). This struct would probably represent a simple shopping item, with a name and a price.

struct ShoppingItem {
    let name: String
    let price: Float
    
    init(name: String, price: Float) {
        self.name = name
        self.price = price
    }
}

And if you wanted to keep a list of these items, you would probably just put them in an array.

let apples = ShoppingItem(name: "Apples", price: 12.76)
let carrots = ShoppingItem(name: "Carrots", price: 15.43)
let bananas = ShoppingItem(name: "Bananas", price: 32.53)

let shoppingList = [apples, carrots, bananas]

Then whenever you need, you would just operate things “on the fly”. Need to calculate the total? Let’s just iterate over the array, and add the values. Same if you want to calculate the average price of the items on your list.

var total: Float = 0
for item in shoppingList {
    total += item.price
}

var avg: Float = total / Float(shoppingList.count)

This works fine. It does it’s job. But it’s not ideal. If you want to calculate the calculate the total or average in different places, you will end up having duplicate code everywhere.

But you already know how to fix that, don’t you?

Let’s Just Encapsulate Everything

If you thought of making a new struct, called ShoppingList or something like that, you are already in the right path. You would create a ShoppingList struct that would encapsulate an array of ShoppingItems. In that way, you could just create computed properties to get the total and average price of everything on the list. You’d end up with something like this.

struct ShoppingList {
    
    private let items: [ShoppingItem]

    var count: Int {
        get {
            return items.count
        }
    }

    var total: Float {
        get {
            var total: Float = 0
            for item in self.items {
                total += item.price
            }
            return total
        }
    }
    
    var average: Float {
        get {
            return self.total / Float(self.count)
        }
    }

    subscript(index: Int) -> ShoppingItem {
        return self.items[index]
    }
    
}

That works pretty well, right? the ShoppingItems are nicely encapsulated inside the ShoppingList. We don’t need to access the underlying array because we have a subscript operator that lets us get the elements of the array directly. We can count the number of elements in said list without querying the underlying array directly. We forgot to add an initialisator though, so let’s go ahead and create an initialisator that takes an array:

struct ShoppingList {
    
    private let items: [ShoppingItem]

    var count: Int {
        get {
            return items.count
        }
    }

    var init(array: [ShoppingItem]) {
        self.items = array
    }

    var total: Float {
        get {
            var total: Float = 0
            for item in self.items {
                total += item.price
            }
            return total
        }
    }
    
    var average: Float {
        get {
            return self.total / Float(self.count)
        }
    }

    subscript(index: Int) -> ShoppingItem {
        return self.items[index]
    }
    
}

You could create a better initialisator by using the elipsis (…) operand, so you could pass in a variable amount of shopping items without putting them in array.

init(items: ShoppingItem...) {

}

But later we are going to do something much neater. Trust me on this one!

Fine and dandy. Let’s create a new ShoppingList now:

let apples = ShoppingItem(name: "Apples", price: 12.76)
let carrots = ShoppingItem(name: "Carrots", price: 15.43)
let bananas = ShoppingItem(name: "Bananas", price: 32.53)

let shoppingList = ShoppingList([apples, carrots, bananas])

Getting the total and average of the items on your list is much better now.

print("\(shoppingList.total)")
print("\(shoppingList.average)")

What’s that? You want to iterate over the items? Err. I mean, you can. But you will have to do it with an old fashioned for loop (not even for-in!). We need to get the number of elements in the underlying array, using our count property, and then use a for loop like we all know.

for var i = 0; i < shoppingList.count; i++ {
    print("\(shoppingList[i].name) cost \(shoppingList[i].price)")
}

And with that, you can do anything you want with each item of the shopping list. Yay!

A Better Way of Doing Things

But of course, this code is slightly awkward, and many of us have written a variation of it at some point. Wouldn’t it be nice if we could treat the ShoppingList as an array, and create it using Array-like Syntax? And wouldn’t it be nice to be able to iterate through it using a for-in loop, instead of having to grab the count and then use an old-fashioned for-loop on it?

The good news is that you can! Thanks to the Swift Standard Library and Swift’s focus on Protocols, we can make our code elegantly blend with the features of the language.

Elegant Array Syntax

First, we will talk about one of the simplest Protocol’s in Swift’s Standard Library – ArrayLiteralConvertible.

This protocol allows your own structs, classes, and even enums to be initialised with array-like syntax.

For example, Foundation’s NSSet object, does this:

let mySet: NSSet = ["foo", "bar", "cheese"]

We can create a set by assigning it an array. In this case, Type Annotation is important, otherwise Swift will think we are creating an Array instead.

We can do with ArrayLiteralConvertible. This protocol has only one requirement:

init(arrayLiteral elements: Self.Element...)

First we make ShoppingList comply with this protocol. Then we implement that only method, which is just one assignment. I will also delete the old init method from before:

struct ShoppingList : ArrayLiteralConvertible {
    //...

    /// MARK: - ArrayLiteralConvertible
    
    init(arrayLiteral: ShoppingItem...) {
        self.items = arrayLiteral
    }

    //...
    
}

Like you can see, it’s really simple – We assign the protocol, we implement the init, and assign the passed array in that init.

With this, you can already create shopping lists neater:

let apples = ShoppingItem(name: "Apples", price: 12.76)
let carrots = ShoppingItem(name: "Carrots", price: 15.43)
let bananas = ShoppingItem(name: "Bananas", price: 32.53)

let shoppingList: ShoppingList = [apples, carrots, bananas]

Just like Apple’s NSSet, the only catch is we need to use type annotation.

Making our Struct Iterable

Finally, we need to make the shopping list enumerable so we can treat it as an array. The work around from above works, but we can definitely make that neater.

Making the struct enumerable is slightly more complicated and involved than adding the natural array initialisation syntax. I will try to explain everything that is going on. You will need to make your struct comply with both SequenceType and GeneratorType

SequenceType

SequenceType’s only job is to make sure you can use your objects in for-in loops. If you check SequenceType’s documentation (linked above), you will see that it has many methods and one typealias. We do not need to comply with all, and in this post, I won’t. You just need to comply with the typealias, and the filter and generate methods:

struct ShoppingList : SequenceType, ArrayLiteralConvertible {
    //...
    
    /// MARK: - SequenceType
    
    typealias Generator = ShoppingList
    
    func filter(includeElement: (ShoppingItem) -> Bool) -> [ShoppingItem] {
        var fItems = [ShoppingItem]()
        for itm in self.items where includeElement(itm) == true {
            fItems += [itm]
        }
        return fItems
    }
    
    func generate() -> Generator {
        return self
    }

    //...
}

According to Swift’s docs, “A generator is a sequence that is consumed when iterated”. To be completely honest with you, I couldn’t make total sense of that definition of generator, but after playing with this for a while, I came with my own definition of generator: A generator gives you the items that are being iterated through. Each time an iteration passes, you are given one item.

The typealias needs to be defined. Like you can see, it is part of the signature of the generate() method, and we are returning self. Why? Well, we can say ShoppingList is a generator because it contains the ShoppingItems, and we want to receive one shopping item each time for-in goes through it.

The filter method is the interesting part. Not all items “match” the filter for-in uses when iterating (I will talk about why later – bear with me for now). So we need to manually check all the items against the filter closure that this method gives us. We are using slight pattern matching here to do just that. At the end of this method, we return an array of elements that match our filter.

GeneratorType

Finally, the last protocol to make our struct enumerable is GeneratorType. Again it has very few requirements, and it’s one of the easiest to implement. You can think of it’s only job to give whoever is requesting it’s data the corresponding element.

    //...
    /// MARK: - GeneratorType
    
    var currentElement = 0
    
    mutating func next() -> ShoppingItem? {
        if currentElement < self.items.count {
            let curItem = currentElement
            currentElement++
            return self.items[curItem]
        }
        return nil
    }

    //...

next() is declared as mutating, because it alters the internal state of our struct. We need to manually keep track of all the iterated items so we can give the right ones at all times.

And that’s it! Making your objects comply with those protocols make it possible to have a nice object that can be initialised an array, has it’s own methods, and can be iterated through with the natural for-in syntax.

Wrapping Up

Before I finish this post, let me tell you what the filter of SequenceType is for – Pattern Matching. for-in, in Swift 2, supports Pattern Matching. When you are using for-in with pattern matching in Swift, the filter block has all the logic to make sure you don’t include incorrect elements.

For example, we can use pattern matching to iterate only through the items whose cost is under 13:

for item in shoppingList where item.price < 13 {
    print("\(item.name) cost \(item.price)")
}

This prints: “Apples cost 12.76” and nothing else, and this behaviour was given for free by just correctly implementing the filter method of SequenceType.

You can download the code for this post from here and copy-paste it in a new Playground.

And with that, I encourage to take a look at Swift’s Standard Library in more depth. There’s many interesting protocols you can adopt to make your code neater and more Swift-like. Currently, there is not much documentation about them besides Apple’s official documentation, so if you find an interesting one, I encourage you to write about it.

Positive SSL