Protocol Extensions in Swift 2.0 for Beginners

Let's try to understand Protocol Extensions using a very simple story. Let's assume that we manage a circus which consists of different animals. One of those animals is a Dog. In order to represent dog we created a struct as defined below: 

struct Dog {
  
}

The Dog struct looks pretty boring! Let's add some functions to the struct. 

struct Dog {

    func eat() {
        print("I like to eat")
    }
    
    func sleep() {
        print("I like to sleep")
    }
    
}

Now, our Dog instance can eat and sleep using the following code: 

let dog = Dog()
dog.eat()
dog.sleep()

Everything is looking good! Your circus show was gaining attraction so you thought why not introduce a cat. Just like Dog struct you added a Cat struct as shown below: 

struct Cat {

  func eat() {
        print("I like to eat")
    }
    
    func sleep() {
        print("I like to sleep")
    }  
}

Something does not feel right about this implementation! I swear I have seen the eat and sleep functions somewhere else. Oh yeah! I have seen that in the Dog struct. DAMN this duplicating code! I can remove this duplicate code if I can find something common about the Dog and Cat. Hmm... they both have four legs, they both have a tail, they both like to eat grass ... thinking..thinking..thinking...... GOT IT! Both Dog and Cat can belong to a base struct called "Animal", since they both are animals. SWEEEEET!!

struct Animal {
    
    func eat() {
        print("I like to eat")
    }
    
    func sleep() {
        print("I like to sleep")
    }
}

Now, all I have to do is to inherit my Dog and Cat structures from the Animal structure and I will automatically get the eat and sleep features. 

struct Dog : Animal { 
}

Unfortunately, structures cannot inherit from non-protocol types. In other word structs cannot have base structs. This is an outrage! At this point you say FU** IT! I am using classes instead of structures. 

So, you changed all the structures into classes as shown below: 

class Animal {
    
    func eat() {
        print("I like to eat")
    }
    
    func sleep() {
        print("I like to sleep")
    }
}

class Dog : Animal {

    
}

class Cat : Animal {

    
}

let dog = Dog()
dog.eat()
dog.sleep()

let cat = Cat()
cat.eat()
cat.sleep()

SWEET!!! Now, my code is much cleaner! My dog and cat can really enjoy the circus. 

After a while someone mentioned that I can enhance my circus using birds. Ironically, I love birds so I decided to add birds to my code... YAWN.. How hard can it be? 

class Bird : Animal {
    
}

Consider it done! Oh wait I forgot to give birds the flying power! This is simple, I will just add a fly method in the Animal class as shown below: 

class Animal {
    
    func eat() {
        print("I like to eat")
    }
    
    func sleep() {
        print("I like to sleep")
    }
    
    func fly() {
        print("I can fly..")
    }
}

Unfortunately, adding to the Animal base class would mean that Dog and Cat entities will also have the flying capability as shown below: 

let bird = Bird()
bird.fly()

let dog = Dog()
dog.fly()

let cat = Cat()
cat.fly()

Well, maybe I can add that only to the Bird class. Let's do that: 

class Bird : Animal {
    
    func fly() {
        print("I can fly..");
    }
}

Sweet! Now only birds can fly and Dog and Cat cannot fly. And you are right it does works as expected! Unfortunately, you forgot about the Penguins that are shipped from Arctic. No problem Penguins are birds.. right. 

class Penguin : Bird {
    
}

DAMMIT! Penguins cannot fly! It would be nice if I have some contract which would indicate if the bird can fly or not. In Swift we can use protocols to create those contracts. 

protocol Flyable {
    func fly()
}

class Eagle : Bird, Flyable {
    func fly() {
        print("I can fly")
    }
}

class Penguin : Bird {
    
}

Now, Eagle can fly and Penguin cannot fly! Sweet! 

At this point your circus is getting a lot of popularity and you decided to include some more birds including Pelican, King Fisher, Sparrow etc. 

protocol Flyable {
    func fly()
}

class Eagle : Bird, Flyable {
    func fly() {
        print("I can fly")
    }
}

class Pelican : Bird, Flyable {
    func fly() {
        print("I can fly")
    }
}

class Sparrow : Bird, Flyable {
    func fly() {
        print("I can fly")
    }
}

Hmm.... Looks like repeatition! Eagle, Pelican and Sparrow can all fly. It would be nice to have some default implementation for the fly function which would be available to all the class that inherit the "Flyable" protocol. This is where "Protocol Extensions" jumps in. I know this article is like a movie where the main characters appears 10 minutes before the end of the movie. 

protocol Flyable {
    func fly()
}

extension Flyable {
    func fly() {
        print("I can fly")
    }
}

class Eagle : Bird, Flyable {
   
}

class Pelican : Bird, Flyable {
   
}

class Sparrow : Bird, Flyable {
   
}

Now, using the Protocol Extension we have a default fly method implementation. Anytime you call the fly method from the class which conforms to the Flyable protocol the default implementation will be called UNLESS the class itself defines its own implementation for the fly method. 

Also, you don't need those darn classes you can simply use structs and increase performance of your app as shown in the complete code below: 

//: Playground - noun: a place where people can play

import UIKit


protocol AnimalBehavior {
    
    func eat()
    func sleep()
}

extension AnimalBehavior {
    func eat() {
        print("I like to eat")
    }
    
    func sleep() {
        print("I like to sleep")
    }
}

struct Dog : AnimalBehavior {

    
}

struct Cat : AnimalBehavior {

    
}


protocol BirdBehavior : AnimalBehavior {
    
}

protocol Flyable {
    func fly()
}

extension Flyable {
    func fly() {
        print("I can fly")
    }
}

struct Eagle : BirdBehavior, Flyable {
   
}

struct Pelican : BirdBehavior, Flyable {
   
}

struct Sparrow : BirdBehavior, Flyable {
   
}

struct Penguin : BirdBehavior {
    
}

let dog = Dog()
dog.eat()
dog.sleep()

let cat = Cat()
cat.eat()
cat.sleep()