Exploring protocols and protocol extensions in Swift

Published on: June 29, 2015

In 2015 Apple announced Protocol extensions at WWDC and went on to explain the idea of Protocol Oriented Programming (video here), I think every iOS developer got really exited when they saw this.

The ability to add default implementations to protocol methods through extensions makes it seem like everything we will ever build can and should be protocol based instead of inheritance based like we do in classical OOP. In this post, we'll explore protocols and protocol extensions to see what we can make them do.

Taking advantage of Protocols today

To take advantage of the awesomeness of Protocols we could think of a Protocol as a mixin more than an interface. I've seen people compare Protocols to interfaces in, for instance, Java. While they are similar they will become quite different once we get to use protocol extensions.

If we can provide default implementations for our Protocol methods we should look at Protocol as little mixins of functionality that can be used to enhance existing types. Mixins are not exactly new in programming and if you look at functional programming mixins are considered pretty important. The cool thing about a mixin (or Protocol) driven approach is that it avoids complex, confusing and inflexible class hierarchies. Instead you can provide multiple Protocols, or mixins, to compile an object with. By doing this, the object will be very explicit about what it can and can't do and the object type itself won't matter throughout your code. What does matter throughout your code is the fact that an object contains certain functionality. And to make sure that an object has certain functionality it conforms to a protocol you wrote. Since this could be pretty confusing if you don't use Protocol oriented programming yet, or if you've never heard of mixin based programming (or even Python's multiple inheritance model), we should probably look at an example now.

An example

So let's imagine that we're mapping out some part of the animal kingdom, let's start with an Animal class. This class will only contain the name of the animal and it's size:

class Animal {
  var name: String?
  var size: AnimalSize?
}

A subclass of animal will be Bird. A Bird will have an amount of legs and it will have a number of wings:

class Bird: Animal {
  var legs = 2
  var wings = 2
}

Now we'll also create a Reptile class which will have a favoriteTemperature property so we know when the Reptile will be nice and comfortable.

class Reptile: Animal {
  var favoriteTemperature = 37.0
}

A subclass of Reptile is a Crocodile, technically this is probably very incorrect, but that's okay, in our imaginary animal kingdom anything goes. The Crocodile will get a number of legs property, just like the bird. It won't get the wings though, flying crocodiles would be too scary for me.

class Crocodile: Reptile {
  var legs = 4
}

As you might have noticed both Bird and Crocodile have a legs property. But if we were to create an application where somebody could pass any Animal they'd like and we'd try to make it walk we could do a check similar to this:

func walk(animal: Animal) {
  if let a = animal as? Bird {
    print("walking an animal with \(a.legs) legs")
  } else if let a = animal as? Crocodile {
    print("walking an animal with \(a.legs) legs")
  }
}

But this approach is prone to error because if we add a new animal with legs we will need to add a new section to our if statement to make sure that we allow that legged animal to walk. Not very developer friendly and basically a bug waiting to happen. We can do better than this by using protocols. Let's refactor the Bird and Crocodile to implement a LeggedAnimal protocol:

protocol LeggedAnimal {
  var legs: Int { get set }
}

class BetterBird: Animal, LeggedAnimal {
  var legs = 2
  var wings = 2
}

class BetterCrocodile: Reptile, LeggedAnimal {
  var legs = 4
}

All I did was define a protocol that forces implementers of that protocol to add a legs property to their object that can be get and set. That's exactly what Bird and Crocodile already did so all we need to do is tell the outside world that we have implemented the LeggedAnimal protocol. Now we can also refactor the walk function:

func betterWalk(animal: LeggedAnimal) {
  print("walking an animal with \(animal.legs) legs")
}

Now that we have a protocol we can just make sure that anything we pass to the function is an implementer of the protocol we just wrote. Much cleaner and a lot less error prone. If we add new animals we just need to make sure that we make them comply with the LeggedAnimal protocol if we want them to be able to walk.

Wrapping up

In my own programs I only started making more use of protocols recently. I usually use them to make completely unrelated things relate to each other just like we did with the LeggedAnimal example. It's a small protocols like this that allow for very flexible modeling and less worrying about subclasses and superclasses. Less inheritance usually means less unused code in classes and that's a good thing.

The only downside at this moment is that you still have to provide an implementation of the required properties and methods. In swift 2.0 this will change because then you will be able to provide a default implementation via protocol extensions. These extensions will allow for even more flexible, DRY and powerful code. I'm looking forward to using those in my projects soon.

You can get the example code from this blogpost from my GitHub.

Categories

Swift

Subscribe to my newsletter