Swift Generics and Protocols – Playground Notes

This post is a summary of some playground work with Swift Generics and Protocols. It also has a couple of tips I learned while in the playground.

If you are reading this and are feeling new to Generics but have even a little Swift programming experience,  then you’ve very likely been using Generics without realizing it. This is because Array, Dictionary, and Set are all generic collections that accept any type you can define in Swift.

I wanted a Container class that could accommodate a collection of like objects, with the caveat that I want to be able to add and remove the objects, and also remove the smallest object from the Container. However, smallest can be defined differently for any type of object that I’m storing in the Container.

First I started out defining the Container to take any generic type T that conforms to Comparable, and defined an array of type T to store the items that can be added to the Container. I also defined the add method.

Based on that, a Container can be instantiated to store any type, provided the type conforms to Comparable. For example, it could have instances of Double or String, since those types conform to Comparable.

The compiler will replace T with the actual type defined, so if you attempt to add a String to con1, the compiler will catch it.

At this point I encountered the fact that Swift Array doesn’t have a method available to simply remove a known object or collection of objects from the array. It has removeAtIndex, but that isn’t what I wanted. I came across this excellent tip from Paul Solt, which is an extension on Array that provides the ability to remove one or more objects from an array by specifying the actual object(s).

Awesome, so now I can add both the standard remove method, as well as the removeSmallest method. I just need to update the Container so it looks like follows.

Note that minElement can be used to return the minimum value of a sequence of Comparable elements, and if the object is found, removeObject from the extension is used to remove the object.

That is all good so far, but I also want to be able to add my own custom objects to the Container. To do that, I defined a Product class.

Every Product will have a name and price. You’ll notice that Product conforms to the Comparable protocol, or at least we’ve said that it will.

This definition alone doesn’t conform to Comparable because we didn’t yet specify how to compare two Product instances, so you’ll see compile errors in the class above. Here are the functions we need to add.

As the comments indicate, you only need to implement == and < to conform to Comparable. Note that Comparable inherits from Equatable, so implementing == is needed based on that inheritance to satisfy the Equatable protocol. That aside, why do only == and < need to be implemented? What happens if we want to use > or != to compare two Product instances?

From the Apple documentationA type conforming to Comparable need only supply the < and == operators; default implementations of <=, >, >=, and != are supplied by the standard library.

If you think about that, the other operator implementations can be inferred based on only == and <, so that functionality is made available automatically.

But wait, those functions we did implement are dangling free all by themselves, so where do they go? The temptation is to put them in the Product class. You can try it in a playground, but you’ll discover that it is incorrect.

In Swift all operators are in global scope, so we overload the operators == and < with functions at global scope, meaning they are outside of the Product type itself.

Now we can add some Product instances to the Container, and then decide to remove the smallest one, which will be based on price.

If you run that in the playground, you’ll see that the two items are added, then the lower priced item is removed.

This playground is available on GitHub. Thanks for reading, and I welcome any comments.

Leave a Comment: