Protocol oriented programming in Swift

As I wrote in a blog post about Value vs Reference types, in Swift Enums and Structs are first class citizens. They contain properties, methods and extensions which are usually only found in Classes. Value types do not support inheritance in Swift, but they can conform to protocols which makes them convenient for use in Protocol Oriented Programming.

They add new functionality to an existing class, structure, enumeration, or protocol type. When you’re working with a certain framework, you might not have access to the actual source code. With extensions you’re able to add new implementation to these existing types i.e. you’re extending these existing types with new functionality (retroactive modeling).

Extensions can add new functionality to a type, but they cannot override existing functionality in pure Swift implementations. Trying to override a superclass method in an extension would lead to a “Declarations in extensions cannot override yet” error. However, the compiler is allowing you to override Objective-C methods in the extension for compatibility reasons, even though it’s actually violating the language directive.

Protocols can also be extended with implementation that is added to conforming types. So instead of adding this functionality to each individual type or in a global function, protocol extension allows you to define behavior on the protocol itself which is consequently added to the conforming type. Protocol extensions are the key to protocol oriented programming.

Providing Default Implementation

Protocol extensions can be used to provide default implementation for any method or computed property requirement of that protocol. Conforming type’s implementation take precedence over the default implementation in protocol extension so if the conforming type provides it’s own implementation for a certain method, that one will be used and not the one that is provided in protocol extension.

Required protocol methods/properties that also have default implementation do not require conforming types to provide implementation, similarly to optional protocol requirements. The difference is that requirements with default implementation can be called without optional chaining.

A major feature of protocol extensions is adding constraints. This is critical to protocol oriented programming. When you define a protocol extension, you can also specify constraints that conforming types must satisfy in order to be able to conform to that protocol. This is achieved using the generic where clause that is defined after the protocol extension name declaration.

Protocol oriented programming solves some of the issues with Object oriented programming. One of the main principles of object oriented programming paradigm – inheritance – is actually one of these issues. A problem with inheritance is that you may choose only one superclass, so you cannot have multiple abstractions. You also have to choose your superclass immediately with the class definition, you cannot do it later in some extension (no retroactive modeling). With protocols you can extend a type whenever it is needed and it can be extended with multiple abstractions. Another problem with inheritance is that you have to accept everything that comes with the superclass, all stored properties that you have to initialize (but might not actually need), you have to be careful not to break the superclass invariants (state). There might be a method that is intended to be overridden, so you have to be careful to do that appropriately. There might be methods that you must not override. There might be methods that you may override but you need to call the superclass, and in that case, you might need to call the superclass at the beginning, maybe you need to call the superclass at the end. There are many things you need to be aware of.

Protocol requirements create customization points

Customizaton points are methods that are both declared and also have the implementation provided immediately in the protocol extension. If the method has default implementation provided immediately in the protocol extension, but does not have it declared, it is not considered a customization point. This is important because when you’re calling this kind of a method on the protocol abstraction and not the concrete type, the protocol implementation will always be used, even if the concrete type also has this method implemented. If the method was declared in the protocol, it would be considered a customization point and it would first be called on the concrete type, if it exists. If it does not exist in the concrete type, only then the default protocol implementation would be used. Here is an example:

This is convenient because some APIs are just not meant to be customization points.

It’s very convenient to make all value types equatable, but this may be a problem e.g. when you have heterogeneous arrays. Here’s an example of how to bridge that gap. This is something that might occur when you get this compiler error:

Binary operator ‘!=’ cannot be applied to two {SomeType} operands.

The idea is to define an isEqualTo function and use default implementation to optionally type cast to self. If that fails the objects are obviously not equal. If it succeeds, than you can compare them with == operator. Here is an example of comparing elements that are composed in a heterogeneous array.

In this example the Diagram struct contains a heterogeneous array of Drawable kind elements. The diagram itself is also of Drawable kind. To check two Diagram elements for equality you need to compare their elements. This is first achieved by checking the number of elements they contain. If the counts are not the same, obviously the elements cannot be equal. If the counts are the same then you can check each pair of elements in both arrays and compare them using the isEqualTo convenience method. If those two elements are not of the same kind, they are obviously not equal. If they are of the same kind, they can be compared with the == operator and that is how it is possible to compare elements that are composed in a heterogeneous array.

If you liked this blog post I’d appreciate if you followed me on Twitter

Related Post

Remember to share...Share on FacebookTweet about this on TwitterShare on Google+Share on LinkedInEmail this to someone

Leave a Reply

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