You can follow the development of this series on Github here.

Welcome back to Writing Testable Swift! In the first part of this series we covered dependency injection, a crucial part of testable code and a great principle for any codebase — even if you’re not writing tests. Today, let’s talk about how we can continue coding confidently with interfaces.

Interfaces

If you’re like me, the first place your mind goes when you hear “interface” is objective-c and header files. Don’t worry! We’re not going to be creating any header files today. Let’s continue with our example from last time and make some more robots!

Today, our boss power-walks into the office and straight to our desk, “We’ve got some clients that need a robot with arms and legs.”

Easy, let’s use what we learned in the previous lesson and inject the parts this robot needs.

Before we could finish, our boss comes back over and says, “Oh yea, and our clients have a few different kinds of arms and legs they want to test out, so make sure that this robot can use any arms that can grab or legs that can walk.”

Sure, we’re object-oriented programmers, we’ve done this sort of thing before. Nothing a little inheritance can’t solve. We write up the abstract base class that our clients should subclass for their own Arms and Legs:

We send this over to our boss and then lounge back in our chair, pleased with our robot-making prowess. About ten minutes later our email starts blowing up with angry messages from our clients:

“We’re using structures to represent our robot’s Arms, we can’t use inheritance!”

And another…

“Our robot’s Legs already have a superclass!”

And another…

“We’re using private, 3rd party robot arms and legs! We can’t change their implementation”

And another…

“Why is our robot crashing while screaming “Must override this method!””

Oh boy, what have we done? Well, we know exactly what we did, we tried to enforce polymorphism through inheritance.

But wait, how could this be wrong? This is how object-oriented programming works! Why is this such an issue?

That’s when we flash back to an important moment in Swift’s history just 3 years ago at WWDC 2015. David Abrahams, the Technical Lead on the Swift Standard Library team, got on stage at the Moscone Center in San Francisco and said,

“When we made Swift, we made the first protocol-oriented programming language.”

There’s a lot of discussion around what protocol-oriented means, but I find it’s best described by another thing David said during the same talk,

“We have a saying in Swift. Don’t start with a class. Start with a protocol”

So let’s follow David’s advice and attack this problem again:

When we use protocols in this way we’re creating an interface. Our interface is telling the rest of our program, “This is how we can communicate.”

Now, when we inject Arms into the robot, all the robot knows is that the Arms can grab. That’s all the robot needs to know about its arms in order to function. It doesn’t know what concrete implementation of Arms is actually conforming to this interface and getting passed into the robot. So now we can do something like this:

As long as our concrete implementations conform to our interface protocols, we can inject them into our robot. Again, all our robot will know is that it has Arms that can grab and Legs that can walk.

Does this solve our clients’ problems? Let’s see:

“We’re using structures to represent our robot’s Arms, we can’t use inheritance!” Great, structures and enums can both conform to protocols in Swift.

“Our robot’s Legs already have a superclass!” All good, classes can conform to as many protocols as they want, even if they’re inheriting from another class.

“We’re using private, 3rd party robot arms and legs! We can’t change their implementation” In Swift we can extend classes we don’t have access to and add protocol conformance.

“Why is our robot crashing while screaming “Must override this method!” This might seem silly, and we might even say, “That’s the client’s fault for not overriding the base class’s method!”. Do you think our boss cares if it’s the client’s fault? Let’s just remove the chance of error by using a protocol which will enforce conformance at compile time.

This protocol-oriented interface is very powerful for a number of reasons:

Multiple Conformance

Unlike inheritance, a single type can conform to multiple protocols. This allows for there to be separate interfaces into a single type, giving us the power of composition over inheritance. Protocols can even inherit from other protocols, a technique we will start using in the next part of this series.

Polymorphism

In Swift, any class, structure, or enum can conform to a protocol. This increases the types of entities that can provide an implementation for a certain interface.

Reduced Coupling

By using protocols, our code is now dependent on an interface instead of a concrete implementation. This means that our interface can mediate the coupling between implementations, which is much easier to modify than tight coupling between two implementations.

If you remember part one of this series, dependency injection also helps to reduce coupling. Reduced coupling is extremely important for the reusability and maintainability of our software.

Testability

Consider this situation:

What if we just want to test the Controller’s userName() method? Look at what the userName() method is doing:

It’s innocently asking our concrete network class to make a network request for a user…

…which in turn asks our server to get that user

…now our server has to do a bunch of work to retrieve that user

…our server will then (hopefully) return a user

…that user will need to get decoded into a User object

…finally our Network class will give our Controller a user

Also, how do we know the name of the user on our server, so we can make sure it’s the same name that our userName() method is returning?

The real question is why do we have to do all of this when I’m just trying to make sure our controller can return some arbitrary user’s name? We’re not trying to test our network!

With dependency injection and protocol interfaces this can be done pretty easily:

Now when we go to test our controller we just inject our TestNetwork and we know that our userName() method should be returning “John”. No network calls or decoding. We’re in complete control.