Embracing the Swift Standard Library.

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.

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.

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.

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:

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.

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:

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:

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.

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:

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.