Object oriented programming (OOP) is one of the most widely used programming paradigms today and it’s hard to imagine the majority of large software projects without it. While OOP is popular, drawbacks such as complicated inheritance and passing around objects to functions by reference (instead of value) lead to an unsafe and hard to maintain code base.

Swift tries to address these OOP drawbacks by introducing a new programming paradigm called Protocol Oriented Programming (POP). I highly recommend watching the WWDC 2015 talk on Protocol Oriented Programming if you haven’t already done so:

Protocol Oriented Programming in

So what are the advantages?

In POP, you can use protocols with structs, enums and classes, make types conform to multiple protocols and provide default implementations for protocols much like how you would if you were creating a base class for inheritance (but better since you aren’t restricted to class types). This makes it easier to encapsulate functional concepts without having to create an inheritance hierarchy. Let me show you what I mean:

I am going to define a protocol called Animal that has name and canSwim properties and another protocol called Swimmable which defines a speed property.

protocol Animal {
    var name: String { get }
    var canSwim: Bool
}

protocol Swimmable {
    var speed: Double { get }
}

In OOP, if you wanted to have an animal who could swim, you would have probably defined Swimmable as the base class and then would have the Animal class inherit from Swimmable. This would lead to a not so ideal situation where you cannot separate Swimmable from Animal.

With POP, I could have an animal Dog that does swim and can have a speed:

struct Dog: Animal, Swimmable { 
    var name: String
    var canSwim: Bool
    var speed: Double {
        return 10.0
    }
}

or an animal Cat that doesn’t swim and so there’s no need for it to have a speed:

struct Cat: Animal {
    var name: String
    var canSwim: Bool
}

In inheritance, you would still have a speed for the Cat.

You can also provide a default implementation to Animal so that the canSwim boolean is automatically calculated. I could now extend the Animal struct and calculate the canSwim boolean like this:

extension Animal {
    var canSwim: Bool {
        return self is Swimmable
    }
}

Practical usage

Here at Rover, our design team has come up with a palette of colors, font weights, font sizes, borders, etc that are reused across the app. As you may know, UIKit doesn’t have the most robust styling support which makes it a pain to reuse styling code. To address this issue, we ended up creating something called RoverCustomViews which uses protocols to bootstrap our own styling system.

First, we created a protocol called BackgroundColored. This protocol has a roverBackgroundColor method that returns a color:

protocol BackgroundColored {
    func roverBackgroundColor() -> UIColor
}

We then created a few specific color protocols that conform to the BackgroundColored protocol and provided default implementations for each:

protocol Color1BackgroundColored: BackgroundColored {}
protocol Color2BackgroundColored: BackgroundColored {}
protocol Color3BackgroundColored: BackgroundColored {}

extension Color1BackgroundColored {
    func roverBackgroundColor() -> UIColor {
        return UIColor.white
    }
}

extension Color2BackgroundColored {
    func roverBackgroundColor() -> UIColor {
        return UIColor.red
    }
}

extension Color3BackgroundColored {
    func roverBackgroundColor() -> UIColor {
        return UIColor.blue
    }
}

At this point, we created a class RoverCustomView which checks to see if it conforms to the BackgroundColored protocol and if so, applies the correct color from the default implementation:

@objc open class RoverCustomView: UIView {
    convenience init() {
        self.init(frame: CGRect.zero)
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupView()
    }
    
    required public init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    
    override open func awakeFromNib() {
        super.awakeFromNib()
        setupView()
    }
    
    fileprivate func setupView() {
        if let bg = self as? BackgroundColored {
            self.backgroundColor = bg.roverBackgroundColor()
        } 
    }
}

We then created subclasses for RoverCustomView that conforms to the correct BackgroundColored protocol:

class RoverColor1View: RoverCustomView, Color1BackgroundColored {}  //returns a white background view.
class RoverColor2View: RoverCustomView, Color2BackgroundColored {} //Returns a red background view.
class RoverColor3View: RoverCustomView, Color3BackgroundColored {} //Returns a blue background view.

With this in place, every time we need to use a view with a specific background color (in interface builder or in code), we can simply specify the correct subclass of RoverCustomView to get the correct view. You can, of course, customize this further by adding other protocols such as DropShadowed or CustomCornerRadius. It’s easy to see how you can also apply this paradigm to other UIView subclasses for customizations. The possibilities are endless.

Have any other insights on how to use protocols better? We’d love to hear them… or better yet come work with us, We’re hiring!