Advertisement
  1. Code
  2. Swift
Code

Protocol-Oriented Programming in Swift 2

by
Difficulty:IntermediateLength:MediumLanguages:

Introduction

With the release of Swift 2, Apple added a range of new features and capabilities to the Swift programming language. One of the most important, however, was an overhaul of protocols. The improved functionality available with Swift protocols allows for a new type of programming, protocol-oriented programming. This is in contrast to the more common object-orientated programming style many of us are used to.

In this tutorial, I am going to show you the basics of protocol-oriented programming in Swift and how it differs from object-orientated programming.

Prerequisites

This tutorial requires that you are running Xcode 7 or higher, which includes support for version 2 of the Swift programming language.

1. Protocol Basics

If you aren't already familiar with protocols, they are a way of extending the functionality of an existing class or structure. A protocol can be thought of as a blueprint or interface that defines a set of properties and methods. A class or structure that conforms to a protocol is required to fill out these properties and methods with values and implementations respectively.

It should also be noted that any of these properties and methods can be designated as optional, which means that conforming types aren't required to implement them. A protocol definition and class conformance in Swift could look like this:

2. An Example

To begin, open Xcode and create a new playground for either iOS or OS X. Once Xcode has created the playground, replace its contents with the following:

We define three protocols, each containing a property. Next, we create a structure that conforms to these three protocols. Add the following code to the playground:

You may have noticed that instead of creating a class that conforms to these protocols, we created a structure. We do this to avoid one of the typical problems inherent to object-oriented programming, object references.

Imagine, for example, that you have two objects, A and B. A creates some data on its own and keeps a reference to that data. A then shares this data with B by reference, which means that both objects have a reference to the same object. Without A knowing, B changes the data in some way.

While this may not seem like a big problem, it can be when A did not expect the data to be altered. Object A may find data it doesn't know how to handle or deal with. This is a common risk of object references.

In Swift, structures are passed by value rather than by reference. This means that, in the above example, if the data created by A was packaged as a structure instead of an object and shared with B, the data would be copied instead of shared by reference. This would then result in both A and B having their own unique copy of the same piece of data. A change made by B wouldn't affect the copy managed by A.

Breaking up the DrivableReversible, and Transport components into individual protocols also allows for greater level of customization than traditional class inheritance. If you've read my first tutorial about the new GameplayKit framework in iOS 9, then this protocol-oriented model is very similar to the Entities and Components structure used in the GameplayKit framework.

By adopting this approach, custom data types can inherit functionality from multiple sources rather than a single superclass. Keeping in mind what we've got so far, we could create the following classes:

  • a class with components of the Drivable and Reversible protocols
  • a class with components of the Drivable and Transportable protocols
  • a class with components of the Reversible and Transportable protocols

With object-oriented programming, the most logical way to create these three classes would be to inherit from one superclass that contains the components of all three protocols. This approach, however, results in the superclass being more complicated than it needs to be and each of the subclasses inheriting more functionality than it needs.

3. Protocol Extensions

Everything I showed you so far has been possible in Swift ever since its release in 2014. These same protocol-oriented concepts could have even been applied to Objective-C protocols. Due to the limitations that used to exist on protocols, however, true protocol-oriented programming wasn't possible until a number of key features were added to the Swift language in version 2. One of the most important of these features is protocol extensions, including conditional extensions.

Firstly, let's extend the Drivable protocol and add a function to determine whether or not a particular Drivable is faster than another. Add the following to your playground:

You can see that, when the playground's code is executed, it outputs a value of false as your sedan car has a default topSpeed of 150, which is less than the sportsCar.

Extension output

You may have noticed that we provided a function definition rather than a function declaration. This seems strange, because protocols are only supposed to contain declarations. Right? This is another very important feature of protocol extensions in Swift 2, default behaviors. By extending a protocol, you can provide a default implementation for functions and computed properties so that classes conforming to the protocol don't have to.

Next, we are going to define another Drivable protocol extension, but this time we'll only define it for value types that also conform to the Reversible protocol. This extension will then contain a function that determines which object has the better speed range. We can achieve this with the following code:

The Self keyword, spelled with a capital "S", is used to represent the class or structure that conforms to the protocol. In the above example, the Self keyword represents the Car structure.

After running the playground's code, Xcode will output the results in the sidebar on the right as shown below. Note that sportsCar has a larger range than sedan.

Conditional extension output

4. Working With the Swift Standard Library

While defining and extending your own protocols can be very useful, the true power of protocol extensions shows when working with the Swift standard library. This allows you to add properties or functions to existing protocols, such as CollectionType (used for things like arrays and dictionaries) and Equatable (being able to determine when two objects are equal or not). With conditional protocol extensions, you can also provide very specific functionality for a specific type of object that conforms to a protocol.

In our playground, we are going to extend the CollectionType protocol and create two methods, one to get the average top speed of cars in a Car array and another for the average reverse speed. Add the following code to your playground:

The protocol extension that defines the averageTopSpeed method takes advantage of conditional extensions in Swift 2. In contrast, the averageReverseSpeed function we define directly below it is another way to achieve a similar result utilizing Swift generics. I personally prefer the cleaner looking CollectionType protocol extension, but it's up to personal preference.

In both functions, we iterate through the array, add up the total amount, and then return the average value. Note that we manually keep a count of the items in the array, because when working with CollectionType rather than regular Array type items, the count property is a Self.Index.Distance type value rather than an Int.

Once your playground has executed all of this code, you should see an output average top speed of 183 and an average reverse speed of 21.

Standard Library extensions

5. Importance of Classes

Despite protocol-oriented programming being a very efficient and scalable way to manage your code in Swift, there are still perfectly valid reasons for using classes when developing in Swift:

Backwards Compatibility

The majority of the iOS, watchOS, and tvOS SDKs are written in Objective-C, using an object-oriented approach. If you need to interact with any of the APIs included in these SDKs, you are forced to use the classes defined in these SDKs.

Referencing an External File or Item

The Swift compiler optimizes the lifetime of objects based on when and where they are used. The stability of class-based objects means that your references to other files and items will remain consistent.

Object References

Object references are exactly what you need at times, for example, if you're feeding information into a particular object, such as a graphics renderer. Using classes with implicit sharing is important in situations like this, because you need to be sure that the renderer you are sending the data to is still the same renderer as before.

Conclusion

Hopefully by the end of this tutorial you can see the potential of protocol-oriented programming in Swift and how it can be used to streamline and extend your code. While this new methodology of coding will not entirely replace object-oriented programming, it does bring a number of very useful, new possibilities.

From default behaviors to protocol extensions, protocol-oriented programming in Swift is going to be adopted by many future APIs and will completely change the way in which we think about software development.

As always, be sure to leave your comments and feedback in the comments below.

Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.