Get up to 80 % extra points for free! More info:

Lesson 11 - Protocols (interfaces) in Swift

In the previous lesson, Properties in Swift, we practiced working with properties.

In today's Swift tutorial, we'll be getting back into some more theoretical matters. We're going to unveil yet another utility of the object-oriented programming world! It'll be interfaces, implemented by Swift as protocols.

Interface

When one refers to an object's interface, they are referring to how an object is visible from the outside. We already know that an object contains methods that can be set as private or public. The object's interface consists of its public methods, which is the way we communicate with certain types of objects. We have already dealt with public methods in previous lessons, e.g. the ones we set up for our arena warrior. The Warrior class we made had the following public methods:

  • func attack(enemy: Warrior)
  • func defend(hit: Int)
  • func alive() -> Bool
  • func setMessage(message: String)
  • func getLastMessage() -> String
  • func healthBar() -> String

If we store an instance of the Warrior class into a variable, we would be able to call the attack() or defend() methods on it. Nothing new, right?

As a matter of fact, we are able to declare an interface separately, sort of like we do with classes. We call this creating a protocol. We would then be able to us this protocol defining an interface as a data type.

We'll give it a go, but on a simpler class than the Warrior class we made. We'll start by creating a new project, a console application, naming it Protocols, and adding a simple class. In my opinion, theoretical concepts should be explained using light examples, meaning not as serious. With that all being said, let's make a bird class! Our bird will be able to chirp, breathe and peck. Our Bird class will look something like this:

class Bird {

    func chirp() {
        print("♫ ♫ ♫")
    }

    func breathe() {
        print("Breathing...")
    }

    func peck() {
        print("Peck, peck!")
    }

}

Done! Now, let's move to main.swift and create a bird instance:

let bird = Bird()

Once you've created an instance of the bird class, write whatever you called the instance, in my case, it is bird, and add a dot right after it. Xcode will then display all of its class methods (you can also invoke this menu by pressing Ctrl + Space):

Instance methods in Swift - Object-Oriented Programming in Swift

We now see everything that we can call on the bird instance. The 3 methods we've just implemented are there as well as some others that all objects have from its base.

Now let's create a protocol for our bird. We'll use the protocol keyword to do just that. Protocol names often ends with -able, such as Printable, Convertible, Renderable etc. We can basically assume what functionality the protocol provides. For example, Renderable could mean that we can render it, better said display it as a part of UI.

We'll get by with BirdProtocol though and to make things easier, let's declare it in the Bird.swift file, above the Bird class. Also, let's declare its methods. We'll purposely omit one of them and only add chirping and breathing:

protocol BirdProtocol {

    func chirp()
    func breathe()

}

We don't specify the public modifier because protocols contain public methods only. It wouldn't make sense otherwise since it indicates how to work with an object from the outside.

Let's go back to main.swift and change the line with the bird variable so it won't be longer of the Bird type, but of the BirdProtocol type:

let bird : BirdInterface = Bird()

What the code above means is that in the bird variable, we expect an object that contains the methods specified in the BirdProtocol. Xcode reports an error since the Bird class doesn't implement BirdProtocol yet. Although it does have the needed methods, it must first be instructed to implement the protocol. Let's move to the Bird class and let it implement the BirdProtocol protocol. The process is the same as with inheritance:

class Bird: BirdInterface {
    // ...

When we go back to main.swift, the line with the variable of the BirdProtocol type no longer causes an error. The Bird class correctly implements the BirdProtocol protocol. Meaning that Bird instances can now be stored in variables of this type.

Now just for completeness' sake, let's see what happens when we remove a method from the class, which is required by the protocol, like the chirp() method. Xcode will warn us that the implementation is not complete. Once you have visual confirmation of the interface's dependency on the class, put the method back where it belongs.

Let's write bird with a dot after it. Again, Xcode will offer the following methods:

Protocol methods in Swift - Object-Oriented Programming in Swift

We can see that now we can call only the methods provided by the protocol on the instance. That's because the bird is now a variable of the BirdProtocol type, not the Bird type. Meaning that we cannot call the peck() method because we did not add it to the interface.

Why did we leave it out in the first place, you may ask? Lots of potential reasons, we've already encountered one of them. By using a protocol, we simplify a complex object and expose only the parts needed in a certain part of the program.

Also, I must add that protocols cannot be instantiated. In other words, this code will not work:

// this code won't work
let bird : BirdProtocol = BirdProtocol()

Multiple inheritance

Swift, like most programming languages, doesn't support multiple inheritance. Meaning that we can't inherit one class from more than one other class. Mainly, because method naming collisions could very well occur when multiple classes containing methods with the same name inherit their methods into another class. Multiple inheritance is often worked around using protocols because we are allowed to implement as many protocols in a class as we want. A class of the sort only allows us to work with its instances in ways that we want to. We wouldn't have to worry of what object type it actually is, or what it provides beyond the interfaces.

Now let's add a LizardProtocol to our project. Lizards will be able to breathe and crawl. To make things easier, let's create the protocol in the current Bird.swift file (or you can create a new Swift file):

protocol LizardProtocol {
    func crawl()
    func breathe()
}

Next, we'll apply "multiple inheritance", more accurately, implement multiple protocols by a single class. We'll add a Pterodactyl class to the project. It will implement both the BirdProtocol and LizardProtocol protocols:

class Pterodactyl: LizardProtocol, BirdProtocol {

}

If we right click on the Pterodactyl in the class definition, we can choose "Refactor -> Add Missing Protocol Requirements" in the context menu and Xcode will automatically generate the needed methods. All we have to do is to implement them.

Automatic protocol implementation in Xcode - Object-Oriented Programming in Swift

After having both interfaces implemented, the code will look like this: <#code#> in Xcode is a special placeholder for code:

class Pterodactyl: LizardInterface, BirdInterface {

    func chirp() {
        <#code#>
    }

    func breathe() {
        <#code#>
    }

    func crawl() {
        <#code#>
    }

    func breathe() {
        <#code#>
    }
}

Xcode has generated the breathe() method twice for some reason, in case this happens to you too, just delete on of them.

Now all we have to do is specify what we want each method to do:

func chirp() {
    print("♫ ♫♫ ♫ ♫ ♫♫")
}

func breathe() {
    print("I'm breathing...")
}

func crawl() {
    print("I'm crawling...")
}

That's pretty much it! Now, let's add an instance of the Pterodactyl class in main.swift:

let pterodactyl = Pterodactyl()

Make sure, that it has both the bird and lizard methods. Unfortunately my Xcode went crazy and hinted me three breathe() methods. After quick googling I found that it's not just me having this problem... It's nothing critical though.

We'll stick to protocols for a little while since there is much more to them that we haven't covered.

In the next lesson, Type casting and object hierarchy in Swift, you'll learn more advanced techniques of the object-oriented programming.


 

Previous article
Properties in Swift
All articles in this section
Object-Oriented Programming in Swift
Skip article
(not recommended)
Type casting and object hierarchy in Swift
Article has been written for you by Filip Němeček
Avatar
User rating:
No one has rated this quite yet, be the first one!
Activities