Protocol-Oriented Programming in Swift

Jul 19, 2022swiftparadigms

Swift protocols play a leading role in the structure of standard library and a common method of abstraction. They provide a similar experience to interfaces that some other languages have.

Protocol-oriented programming has become somewhat a fundamental in Swift. It is important you need to grasp following:

  • Difference from object-oriented programming
  • Protocol extensions, protocol inheritance, protocol composition
  • Protocols with associated types
  • Defining protocol-conforming types
  • Extending protocols with default implementations
  • Extending protocols in Apple or third-party libraries
  • Extending protocols conditionally using generic where clauses
  • Checking for protocol conformance (is, as)
  • Define optional requirements for protocols (@objc optional)

Protocols allow you to group similar methods, functions and properties. Swift lets you specify these interface guarantees on class, struct and enum types. Only class types can use base classes and inheritance.

An advantage of protocols in Swift is that objects can conform to multiple protocols.

When writing an app this way, your code becomes more modular. Think of protocols as building blocks of functionality. When you add new functionality by conforming an object to a protocol, you don’t build a whole new object. That’s time-consuming. Instead, you add different building blocks until your object is ready.

Protocol extensions may seem quite similar to using a base class, but there are several benefits of using protocol extensions. These include, but are not necessarily limited to:

  • Since classes, structures and, enums can conform to more than one protocol, they can take the default implementation of multiple protocols. This is conceptually similar to multiple inheritance in other languages.
  • Protocols can be adopted by classes, structures, and enums, whereas base classes and inheritance are available for classes only.

Here is a list of advantages of protocols and how it differs with inheritance:

Features Inheritance Protocols
Interface reuse Inherit from superclass Protocol extensions
Customization Overriding + while maintaining invariants Implement requirements + override defaults
Implementation reuse Inherit from superclass Adopt protocols
Usable with value types No Yes
Modeling flexibility Upfront modeling + exclusive hierachies Retroactive modeling + ad-hoc hierachies

Retroactive Modeling

Rather than having to lock ourselves into inflexible inheritance hierarchies and being forced to settle on key abstractions early in the design process, Swift protocols and protocol extensions enable retroactive modeling. We can start with concrete code and work our way to abstractions when we need to assign a role or a persona to entities or concepts in the domain.

Retroactive modeling is the practice of using existing types to represent new concepts, without modifying those types. This technique is important for reusing existing structures, while maintaining compatibility with the current usage. Swift supports retroactive modeling through the use of extensions.

Extensions enable you to add new functionality to existing types, without the need to have access to the original source code. Swift extensions are similar to categories in Objective-C, and can be used to extend a class, struct, enum, or protocol.

Retroactive modeling saves us from having to decide on key abstractions early in the design process.

Protocol Composition

It can be useful to require a type to conform to multiple protocols at the same time. You can combine multiple protocols into a single requirement with a protocol composition. Protocol compositions behave as if you defined a temporary local protocol that has the combined requirements of all protocols in the composition. Protocol compositions don’t define any new protocol types.

Protocol compositions have the form SomeProtocol & AnotherProtocol. You can list as many protocols as you need, separating them with ampersands (&). In addition to its list of protocols, a protocol composition can also contain one class type, which you can use to specify a required superclass.