(Swift) Understanding Protocol-Oriented Programming

Kenta Kodashima
7 min readOct 6, 2018

--

At WWDC in 2015, the Apple team introduced the powerful concept called Protocol-Oriented Programming(POP). In this article, I try to summarize the concepts from the video of WWDC. If you haven’t watched the presentation, I strongly recommend that you watch the video at least once.

What is Protocol?

The official documentation from Apple defines a protocol as below.

A protocol defines a blueprint of methods, properties, and other requirements that suit a particular task or piece of functionality. The protocol can then be adopted by a class, structure, or enumeration to provide an actual implementation of those requirements. Any type that satisfies the requirements of a protocol is said to conform to that protocol.

In other words, a protocol has some common features among some classes. Classes, structs, or enums conforming to a protocol will have the same kind of functionality. A protocol looks like this.

protocol protocolName {
var property1: String { get set }
var property2: Int { get }
func protocolMethod() -> String
}

Protocol Properties

If you want to declare properties inside of a protocol, you need to stick with its special form of declaration. It looks like this.

var propertyName: type { get set }

There are few differences compared with the typical properties.

  • get and set keyword
    These keywords are used to specify how you want to treat a property. If you put both get and set inside the property’s curly braces, the property will be readable and writable . Additionally, it cannot be a constant in the conforming type declarations since it can be readable and writable.
    If you put only get keyword, on the other hand, it will be a read-only property. In that case, you can declare it as a constant using let in the conforming types.
  • let is not allowed in the protocol declaration
    You already know there is a special form of variable declaration in protocol properties, using get and set . If you don’t want a property to be changed, you can just put get in the protocol declaration. In that way, you can declare the property as a constant in the conforming types.

What is POP?

Protocol-Oriented Programming(POP) is a new approach to programming which addresses the problems related to Object-Oriented Programming. I will explain three major problems of OOP.

Three major problems of OOP

1) Implicit sharing

In OOP, you have to keep track of the instances so that they don’t affect each other. When you create instances of a class, instances have the reference to exactly the same object.

Trying to protect the object from unexpected changes by the instances, you will be defensive about coping with the object. You may use let or static to make properties or methods. The more you get defensive, the more the object loses its flexibility. The more the object loses its flexibility, the more complicated your code gets.

2) Inheritance all up in your business

The inheritance structure itself is not so flexible because of the following reasons.

  • Only one superclass allowed
  • Single inheritance weight gain
    There might be too many instances which share the reference for the object.
  • No retroactive modelling
    When you make a class, you have to choose a superclass right away. It’s not later in some extensions.
  • Superclass may have stored properties
    If the superclass has stored properties, subclasses must just accept them. That kind of a situation leads to the initialization burden.
  • Don’t break superclass invariants
    Know how to interact with its superclass without breaking its invariants.
  • Know what/how to override (and when not to)
    Without using final, the methods may get overridden.

3) Lost type relationships

Classes don’t fit for the situations where type relationships matter. For instance, symmetric operations such as comparison. Let’s take a look at the example from WWDC.

class Ordered {
func precedes(other: Ordered) -> Bool {
fatalError("implement me!")
}
}
class Label: Ordered { var text: String = "" ... }class Number: Ordered {
var value: Double = 0
override func precedes(other: Ordered) -> Bool {
return value < (other as! Number).value
}
}

To put it simply, the code above is trying to compare two double numbers using inheritance. However, this way has a couple of problems.

  • Superclass must have something inside of the method body.
    Even though you just want to have a blueprint of the function, the method body cannot be empty. It just doesn’t make sense that you have to put something although you are not actually using it.
  • Down-casting is needed in each subclass.
    As you can see in the example, the subclass has to down-cast in order to compare value and its argument. It knows value is Double type, however, other argument is an Ordered type.
    The Ordered doesn’t even have value property, therefore, the other argument is needed to be down-casted.

Solving the problems with Protocols

Protocol-Oriented Programming comes in handy in a situation like this. The code below is the Protocol-Oriented version of the previous code.

protocol Ordered { 
func precedes(other: Self) -> Bool
}
struct Number : Ordered {
var value: Double = 0
func precedes(other: Number) -> Bool {
return self.value < other.value
}
}

There are several merits in adopting Protocol-Oriented approach in this case.

  • The body of the method in the superclass has been removed.
    Since protocols don’t need a method body, it has simply been removed. No more waste of lines there.
  • No need for down-casting
    You might have noticed the Self keyword. If you see Self in a protocol it’s just a placeholder for the type that is going to conform to that protocol. This is called Self-Requirement.
    Since Self is going to be the type of Number in this example, there’s no need to down-cast other argument.

Protocol Extensions

At some points, you may want to have a default value for the properties or the methods inside of a protocol. Swift’s extension can make it possible.

protocol Competition {
var prize: String { get set }
func announcePrize(to person: Person)
}
extension Competition {
func announcePrize(to person: Person) {
print("\(person.name) gets... \(self.prize)! Congrats!")
}
}
struct CodingChallenge: Competition {
var prize: String
}
struct Person {
var name: String
}
let codingEvent = CodingChallenge(prize: "A trip to San Fransisco")
let challenger = Person(name: "Kenta")
// Will print "Kenta gets... Trip to San Fransisco! Congrats!"
codingEvent.announcePrize(to: challenger)

Imagine you are imitating competitions in code. All events conforming to Competition protocol will have the prize property and announcePrize(to person:) method. It’s kind of a troublesome task to implement the announcePrize(to person:) every time you make some types which are conforming to Competition . It may be good to have some default announcement style through events.

In the example above, the default announcement style is implemented in the extension. Since Competition has a default implementation of the method, you don’t even need to write the method in the conforming type (in this case CodingChallenge struct). This is how you can implement default values. It seems pretty nice, doesn’t it?

Adding Constraints to Protocol Extensions

Lastly, there is one more feature regarding protocol extensions that I would like to introduce, the constraints. Here is the definition of the constraints from the Swift language website.

When you define a protocol extension, you can specify constraints that conforming types must satisfy before the methods and properties of the extension are available. You write these constraints after the name of the protocol you’re extending by writing a generic where clause. For more about generic where clauses, see Generic Where Clauses.

If you go to check the link(the Swift language website), there is an example using Collection type. Let’s see the example in action.

// An example from the documentation
extension Collection where Element: Equatable {
func allEqual() -> Bool {
for element in self {
if element != self.first {
return false
}
}
return true
}
}
struct Person: Equatable {
var name: String
static func ==(lhsPerson: Person, rhsPerson: Person) -> Bool {
return lhsPerson.name == rhsPerson.name
}
}
let person1 = Person(name: "John")
let person2 = Person(name: "Ringo")
let person3 = Person(name: "George")
let person4 = Person(name: "Paul")
let person5 = Person(name: "John")
var personCollection = [person1, person2, person3, person4, person5]// Will be false
personCollection.allEqual()

First of all, Collection is a protocol which defines behaviours of sequences such as Array, Dictionary, etc.

Notice the code where Element: Equatable right after extension Collection at the top. Because of this code, types which conform to Collection must conform to Equatable too. The function allEqual() compares each element in the given collection and returns true if all elements are equal.

Right below the extension. There is a simple Person class which conforms to Equatable be implementing func ==(lhsPerson:, rhsPerson:) method of Equatable . This method compares given Person ‘s name.

If you test the example in your playground, personCollection.allEqual() will be false because person1 and person5 have the same name.

That’s everything for this article! When I first saw the video, I was touched by knowing how beautifully designed it is. I hope you will feel the same way.

References

WWDC 2015: Protocol-Oriented Programming in Swift
https://developer.apple.com/videos/play/wwdc2015/408/

Introducing Protocol-Oriented Programming in Swift 3
https://www.raywenderlich.com/814-introducing-protocol-oriented-programming-in-swift-3

The Swift Programming Language Swift 4.2
— Declaration #Protocol Property Declaration
https://docs.swift.org/swift-book/ReferenceManual/Declarations.html#ID370

The Swift Programming Language Swift 4.2 — Protocols
https://docs.swift.org/swift-book/LanguageGuide/Protocols.html#ID269

Apple Developer Documentation — Collection
https://developer.apple.com/documentation/swift/collection

Apple Developer Documentation — Equatable
https://developer.apple.com/documentation/swift/equatable

--

--

Kenta Kodashima
Kenta Kodashima

Written by Kenta Kodashima

I'm a Software Engineer based in Vancouver. Personal Interests: Books, Philosophy, Piano, Movies, Music, Studio Ghibli.

No responses yet