Lesson 012 – Intro to Protocols

ProtocolOne of the challenges with writing code is planning for the future. You know what things need to do now (or at least you think you do) and you can work on programming to those things. However, a good programmer does care how the system or application will continue to be built in the future. This is a really dangerous line, however. You want to be open to let your code be extended without you needing to predict the future, because you aren’t a psychic and you’ll be wrong a vast majority of the time.

Enter Protocols

In Swift, Protocols are like Interfaces in a lot of other languages. What they can do is to help keep a “contract” in place in two ways in your code. The first way is that it can ensure that when a class inherits a protocol, that class must implement the functionality described in the protocol. Let’s take a look at a simple example of that. In this example, the protocol doesn’t promise any functionality, so the class doesn’t have to have any functionality. This is the most simple version of the syntax.

However, this isn’t very useful. Instead, let’s make the Car protocol require some things and see what happens if we don’t do them.

In this case, the Car protocol is enforcing that every class that inherits from Car must have a numberOfCylinders property and a color property. Our Camry did not implement those properties, though and Swift throws a couple of errors: “error: type ‘Camry’ does not conform to protocol ‘Car'”, “note: protocol requires property ‘numberOfCylinders’ with type ‘Int'”, “note: protocol requires property ‘color’ with type ‘String'”. In order to satisfy those requirements, we have to implement them like this.

Protocol All The Things!!!

Okay, I’m not really recommending that you over-engineer your code and throw Protocols everywhere just to do it. However, Protocols have some important advantages over traditional Interfaces. One of those advantages is that we can force classes that we don’t own to conform to protocols that their authors did not require.

What we’ve done is create a protocol called BiggieSize and then through the use of Extensions (see Lesson 011), we force the String class to conform to that protocol. This can be a handy trick if you are trying to work with code that you can’t change and you need to allow it to conform to a contract.

The Contract, Part II

Earlier, I talked about keeping a “contract” in place in two ways in your code. We’ve already looked at the first way. The first way was ensuring that classes or structs had certain features and behaviors. The second way is that you can ensure that objects you receive have a minimum set of features. This is the true future-proofing of your system. You can’t possibly know about all of the classes that may one day be created. However, when you write your code, you don’t really care.

You don’t use ALL of the features of an object, often you only access a property or two or call a small set of methods. In that case, you can claim that the parameter to your method be a protocol and not a specific class. That way, any class that conforms to that protocol is allowed in. Let’s create a method that takes a BiggieSize as a parameter and see how that might work.

Because I had added that extension onto String, I am able to just pass any old string in as a BiggieSize. Even if the person who wrote the BiggieSize protocol had never envisioned in the whole wide world that someone would give her a String, that doesn’t matter. Really, the programmer just needed to be able to call a doubleUp method. Since I was able to offer her a doubleUp method, everything is copacetic.

Another Reason Protocols are Cool

This business about protecting a contract is something that interfaces can handle in other languages. However, what if you wanted to make sure that a parameter conformed to two interfaces? To handle that in other languages, you’d often have to use something called a typedef to define this new amalgamation. In Swift, we need no such thing. We can define a parameter’s type as protocol<>, where we put a comma-separated list of protocols inside the angle brackets to specify our “list of demands”. Here is a simple example.

In this example, we have said that we don’t care what you give us in our demonstrateIntersection() method, we just want to make sure that it has a talk() and talkAlso() method. Another thing I threw at you was the line like this in the protocol

Previously, we had only made protocols define properties, but they can define methods, too. All you have to do to define methods is to include their signature (with no implementation) and those methods will be required.

So, let’s see how this looks implemented.

So far, I’ve implemented three classes. One of them only implements the A protocol, one only implements the B protocol, and the third implements them both. Then, I go on and declare three object variables, one for each class. If we try to pass in either the aVariable or bVariable objects to our demonstrateIntersection method, we get errors.

However, if we pass in our bothVariable object, it works perfectly

That prints out FOOOO! and BARRRR!.

Conclusion

That concludes our introduction to Protocols. We’ve seen how Protocols can help us enforce a “contract” in our code that allows us to have the most flexibility for the future by only asking for the things we need in our code. If you have any questions, please leave them in the comments and remember that all of the code for this lesson and all lessons is up on GitHub.

Add Comment

Required fields are marked *. Your email address will not be published.