Home Schedule Syllabus Resources

Lecture 2 - Protocols and Delegates

In this lecture we are going to cover protocols and delegates in Swift. If you've ever programmed in Java or C++, protocols are similar to interfaces or abstract classes. If you don't have experience with Java or C++ and that makes no sense to you, don't worry about it though. Protocols and delegates are pretty easy to learn and prove to be quite useful in iOS development.

We'll also be covering Core Location, Apple's GPS framework, in this week's homework. Core Location uses protocols and delegation a good deal, so it'll be good practice for the concepts you learn in this lecture.

Protocols

We'll start out by covering the basics of protocols. First, you're probably wondering what a protocol is. Well, it's kind of just what it sounds like: some set of rules or plan for carrying out something.

In the context of iOS development, a protocol is a set of "rules" to which a class subscribes. So, by subscribing to a protocol a class promises to do whatever is defined by the protocol. This usually means that the class promises to implement all of the functions and variables defined in the protocol. Let's see an example to try and make more sense of all of this.

Pretend you have a farm with a bunch of animals. All of the animals need to eat, right? That's a rule, so "eat" would be in the FarmAnimal protocol. And each animal has a name, so "name" would be in the FarmAnimal protocol too.

Different animals have different ways of handling these rules. Let's say there's a cow named Otis. It's time for Otis to eat, so since he's a cow he eats some grass. Now let's say there's a horse named Pegasus. It's time for Pegasus to eat, so since he's a horse he eats hay. See how it depends on the type of animal for how the rule "eat" is carried out?

Let's see what this set of rules might look like in code:

protocol FarmAnimal { 
  var name: String { get set }  // name of the animal
  var food: String { get }      // food that the animal eats
  func eat()
} 

First, to declare a protocol (the set of "rules") you use the keyword protocol and then whatever you want to name the protocol.

Variables that you want to force as rules are declared the same exact way that they are in classes. The only difference above is the part that says get and set. This is basically to say that this variable should be gettable (that is, other classes should be able to read this member variable) and settable (other classes should be able to set this member variable).

Functions are declared as stubs--identical to the way they're written in classes, just without the function body.

So, classes that subscribe to FarmAnimal absolutely must have a member variable, name, which is both gettable and settable. They will also have another member variable, food, which must be at least gettable. They must also have a function, eat, which takes in nothing and returns nothing. These are rules that classes must, must, MUST obey when they subscribe to this protocol. Otherwise, Xcode complains and won't compile the code.

Another thing to note is that when you define a protocol, the protocol becomes a type. This type cannot be instantiated (it's like an abstract class: the protocol doesn't have any implementations...), but objects that implement the protocol can be cast to the type, which is very helpful as we will see later.

So, let's show an example of a class that subscribes to this protocol:

class Cow: NSObject, FarmAnimal {
    
    var name: String
    var food: String = "grass"
    
    init(name: String) {
        self.name = name
    }

    func eat() {
        println("Yum I just ate some \(self.food) because I'm a cow.")
        nap()
    }
    
    func nap() {
        println("Now I'm going to take a nap because I'm a cow.")
    }
}

Now we create the Cow class which implments the FarmAnimal protocol. See how the function and variables that are delcared in the protocol are all implemented in this class? If that wasn't the case, Xcode would complain at compile-time that the Cow class does not properly implement FarmAnimal.

Now, we can have a totally different class subscribe to this protocol:

class Horse: NSObject, FarmAnimal {
    
    var name: String
    var food: String = "hay"
    
    init(name: String) {
        self.name = name
    }
    
    func eat() {
        println("Wahoo I just ate some \(self.food) because I'm a horse.")
        trotAround()
    }
    
    func trotAround() {
        println("Now I'm going to trot around because I'm a horse.")
    }
}   

The Horse class successfully subscribes to FarmAnimal. It fits all the rules that are set by that protocol, but does it in a different way to the Cow class.

However, the Cow and Horse class have something in common: they both subscribe to FarmAnimal. Therefore, we should be able to, in some respects, treat both of these classes as objects of the same type. We can do that by casting both of these classes to FarmAnimal, like so:

var cow : FarmAnimal = Cow(name: "Otis")
var horse : FarmAnimal = Horse(name: "Pegasus")
var farmAnimals: [FarmAnimal] = [cow, horse]

for animal in farmAnimals
{
  animal.eat()
} 

So now observe that instances of classes which implement FarmAnimal can be of type FarmAnimal. This can be very helpful at times. Say you want to feed all your farm animals at once. You can call all the farm animals a FarmAnimal, put them in an array, and then just iterate over all the animals and call eat. This is much easier and cleaner than feeding each animal individually.

In summary, protocols are a set of functions and variables which act as rules. When a class implements a protocol, it is promising that it will implement all of the rules in the protocol.

Delegates

Now that we have covered protocols, we will talk about delegates. To start, we'll discuss the idea of delegation. Delegation is a design pattern. If you aren't sure what that means, no worries. Basically, they are structures or outlines for organizing code to solve common problems. In this case, delegation is when an object hands off some of its work to another object. That means you will have a class that contains a delegate object, usually. To get a better idea of what this means, let's apply delegation to the farm animal example.

So, for example, the Cow class had a "rule" that they had to eat. Cow decided exactly how it would eat. But realistically we know that Cows are kind of helpless and it is actually your duty as the farmer to feed your Cows. Therefore, you will help do some work for the cows by providing them with food so they can eat. You are the Cow's delegate.

So, first we will create a protocol to set the rules for what our delegate will do:

protocol CowDelegate {
    func feedCow(cow: Cow)
}  

So, classes that implement this protocol will be responsible for feeding a Cow. So now, we will create a CowFarmer class that subscribes to this protocol and actually feeds the cows.

class CowFarmer: CowDelegate {
    func feedCow(cow: Cow) {
        println("Feeding \(cow.name) some \(cow.food)...")
    }
} 

So now Cow should have a member dedicated to its delegate, so that Cow can call upon it when it's time to eat:

class Cow: FarmAnimal {
  
  var name: String
  var food: String = "grass"
  var delegate: CowDelegate?
  
  init(name: String, delegate: CowDelegate) {
    self.name = name
    self.delegate = delegate
  }
  
  init(name: String) {  // this is a designated initializer. Why?
    self.name = name
  }

  func eat() {
    self.delegate?.feedCow(self)
    nap()
  }
  
  func nap() {
    println("Now I'm going to take a nap because I'm a cow.")
  }
}

In Line 12, we see some weird syntax: self.delegate?.feedCow(self). This is convenient shorthand for saying: if self.delegate is not nil, call feedCow(self); otherwise, do nothing.

So, we could see this in action:

var cowFarmer = CowFarmer()
var cow = Cow(name: "Joe", delegate: cowFarmer)
cow.eat()

We should see "Feeding Joe some grass...", followed by "Now I'm going to take a nap because I'm a cow". Now, because CowDelegate is a protocol, any class can subscribe to it. Even a BadCowFarmer class like this:

class BadCowFarmer: CowDelegate {
    func feedCow(cow: Cow) {
        println("Feeding \(cow.name) some moldy cheese...")
    }
}   

There is so much wrong with that. But seriously take some time and let this design pattern sink in.

In summary, a delegate is an object that does work for a certain class. In Swift, it implemented with protocols in order to force the desired behavior. This example with farm animals is pretty simple, but protocols and delegates can be quite helpful when designing an app. We will also see this later in Core Location.