Swift protocol

The swift protocol is basically a contract that a class/struct/enum can adopt to implement a specific set of methods and properties.

Swift Protocol Syntax

The syntax of Swift Protocol is similar to classes, structures, and enums.

protocol MyProtocol {
    
    //Properties and methods declarations go in here.
    
}

classes, structs, and enums can adopt the protocol by placing it after the colon as shown below.

class MyClass: MyProtocol {
    //Enter your code
}

struct myStruct: MyProtocol {
    //Enter your code
    
}

enum MyEnum: MyProtocol {
    //Enter your code
}
  1. If there are more than one protocols, they’re separated by commas.
  2. If a class has a superclass, the superclass name would appear first, followed by the protocols.
protocol MySecondProtocol {
    //Properties and methods declarations go in here.
}

class MySuperClass {
    //Let's say this is the superclass.
}

class MySubClass: MySuperClass, MyProtocol, MySecondProtocol{
    //Enter your code
}

struct MyStruct: MyProtocol, MySecondProtocol{
    //Enter your code
}

enum MyEnum: MyProtocol, MySecondProtocol {
    //Enter your code
}

When a class/struct or enum conforms to a protocol, it must implement all of its methods to compile. Let’s dive into the deep world of Protocols by starting our Playground in Xcode.

Swift Protocol Requirements

  1. A protocol can contain properties as well as methods that the class/struct/enum conforming to the protocol would implement.
  2. First things first, variables inside a protocol must be declared as a variable and not a constant(let isn’t allowed).
  3. Properties inside a protocol must contain the keyword var.
  4. We need to explicitly specify the type of property( {get} or {get set}).

The following are the two ways to do so.

protocol MyProtocol {
    
    var age: Int {get set} //read-write property
    var year: Int {get} //optional write property
    
}

A {get set} computed property is readable as well as writable. For a {get}, it need not be settable. In case the user wants to ensure that it’s readable only, they can change the property to a constant in the class.

class MyClass: MyProtocol {
    
    var age = 23
    var year = 2019
    
}

var a = MyClass()
a.year = 2020
print(a.year) //prints 2020
  1. Functions(Swift Function) inside a protocol are defined without a body.
  2. Default values within functions aren’t allowed.
  3. Functions with variadic parameters are also allowed.
  4. Mutating functions can be defined inside a keyword by adding the keyword mutating.
  5. We do not need to write the mutating keyword when writing an implementation of that method for a class.
  6. The mutating keyword is only used by structures and enumerations.

Swift Protocol Example

Let’s look at an example that uses the above swift protocol concepts.

import Foundation

protocol MasterDegree {
    
    var gpa: Double { get set }
    var name: String? { get set }
    var coursesCompleted: Bool? { get set }
    func haveYouCompletedOperatingSystems() -> Bool
    func haveYouCompletedSwiftCourse() -> Bool
    func printAllGrades(grade: String...)
    func degreeGranted()
    func hackToGetMyDegree()
    
}

class Student: MasterDegree {

    var gpa: Double = 2
    var name: String?
    var coursesCompleted: Bool?

    func haveYouCompletedSwiftCourse() -> Bool {

        return coursesCompleted ?? false
    }

    func haveYouCompletedOperatingSystems() -> Bool {

        return coursesCompleted ?? false
    }

    func printAllGrades(grade: String...) {
        if let n = name {
            print("Grades of \(n) in all the courses are \(grade)")
        } else {
            print("Student not found")
        }
    }

    func degreeGranted() {
        if haveYouCompletedSwiftCourse() &&
            haveYouCompletedOperatingSystems() &&
            gpa > 4 {
            print("Congrats, you've completed Master in Computer Science")
        } else {
            print("Sorry, you won't be granted your degree this year")
        }
    }

    func hackToGetMyDegree() {
        gpa = 8.5
        coursesCompleted = true
    }

}

var s = Student()
s.gpa = 6.93
s.name = "Balu Naik"
s.coursesCompleted = false
print(s.name ?? "Not found") //prints "Balu Naik"
s.haveYouCompletedOperatingSystems()
s.haveYouCompletedSwiftCourse()
s.degreeGranted() //Sorry, you won't be granted your degree this year
s.printAllGrades(grade: "A","D") //Grades of Balu Naik in all the courses are ["A", "D"]

Oops! Since my coursesCompleted boolean property is false, the degreeGranted() method isn’t giving me my degree. Let’s alter the property using the mutating function as shown below.

s.hackToGetMyDegree()
s.degreeGranted() //Congrats, you've completed Master in Computer Science

Similarly, we can use the protocol in a Structure as shown below.

import Foundation

protocol MasterDegree {
    
    var gpa: Double { get set }
    var name: String? { get set }
    var coursesCompleted: Bool? { get set }
    
    func haveYouCompletedOperatingSystems() -> Bool
    func haveYouCompletedSwiftCourse() -> Bool
    func printAllGrades(grade: String...)
    func degreeGranted()
    
    mutating  func hackToGetMyDegree()
    
}

struct Student: MasterDegree {

    var gpa: Double = 2
    var name: String?
    var coursesCompleted: Bool?

    func haveYouCompletedSwiftCourse() -> Bool {

        return coursesCompleted ?? false
    }

    func haveYouCompletedOperatingSystems() -> Bool {

        return coursesCompleted ?? false
    }

    func printAllGrades(grade: String...) {
        if let n = name {
            print("Grades of \(n) in all the courses are \(grade)")
        } else {
            print("Student not found")
        }
    }

    func degreeGranted() {
        if haveYouCompletedSwiftCourse() &&
            haveYouCompletedOperatingSystems() &&
            gpa > 4 {
            print("Congrats, you've completed Master in Computer Science")
        } else {
            print("Sorry, you won't be granted your degree this year")
        }
    }

    mutating func hackToGetMyDegree() {
        gpa = 8.5
        coursesCompleted = true
    }

}

var s = Student()
s.gpa = 6.93
s.name = "Balu Naik"
s.coursesCompleted = false
print(s.name ?? "Not found") //prints "Balu Naik"
s.haveYouCompletedOperatingSystems()
s.haveYouCompletedSwiftCourse()
s.degreeGranted() //Sorry, you won't be granted your degree this year
s.printAllGrades(grade: "A","D") //Grades of Balu Naik in all the courses are ["A", "D"]

s.hackToGetMyDegree()
s.degreeGranted() //Congrats, you've completed Master in Computer Science

Swift Protocols With Initializers

We can implement a swift protocol initializer requirement on a conforming class as either a designated initializer or a convenience initializer. For this, we must add the keyword required before init in the conforming class as shown below:

import Foundation

protocol MyInitialiser {
    
    init(name: String)
}

class TestInitProtocol: MyInitialiser {
    
    required init(name: String) {
        print("Welcome to \(name)")
    }
}
var t = TestInitProtocol(name: "Balututorial") //prints Welcome to Balututorial

Convenience initializer example for protocol initializer

import Foundation

protocol MyInitialiser {
    
    init(name: String)
    
}

class TestInitProtocol: MyInitialiser {
    
    convenience required init(name: String) {
        print("Welcome to \(name)")
        self.init()
    }
}

var t = TestInitProtocol(name: "Balututorial")

Using Protocol Initialisers along with initializers from SuperClass

import Foundation

protocol MyInitialiser {
    init(name: String)
}

class SuperClass {
    
    init(name: String) {
        print("i'm in super class: \(name)")
    }
    
}

class TestInitProtocol: SuperClass, MyInitialiser {
    
    required override init(name: String) {
      print("required from protocol, override from superclass")
        super.init(name: name)
    }

}

let t = TestInitProtocol(name: "Balututorial")

Swift Protocols Extensions

We can use Protocols on Extensions to add new properties, methods on an existing type.

import Foundation

protocol Greet {
    var str: String { get }
}

class Name {
    var name: String?
}

extension Name: Greet {
    
    var str: String {
        return "Hello, \(name ?? "Mr. X")"
    }
}

var n = Name()
n.name = "Balu Naik"
print(n.str) //prints Hello, Balu Naik

Protocol Extensions

Protocols can be extended to provide method and property implementations to conforming types. This way we can specify default values for properties in a protocol too as shown below.

import Foundation

protocol WebsiteName {
    var name: String {get}
}

extension WebsiteName {
    var name: String {
        
        return "Balututorial"
    }
}

class Website: WebsiteName {
    
}
var w = Website()
print(w.name) //prints Balututorial

Swift Protocols with Enums

Consider the following case where a Protocol is implemented in an enum.

import Foundation

protocol ModifyCurrentDay {
    
    func currentDay() -> String
    mutating func firstDayOfTheWeek()
    
}

enum DaysOfAWeek: String, ModifyCurrentDay {
    case Sunday
    case Monday
    case Tuesday
    case Wednesday
    
    func currentDay() -> String {
        
        return self.rawValue
    }
    
    mutating func firstDayOfTheWeek() {
        self = .Sunday
    }
}
var day = DaysOfAWeek.Wednesday
print(day.currentDay()) //prints Wednesday
day.firstDayOfTheWeek()
print(day.currentDay()) //prints Sunday

Swift Protocols Inheritance

A protocol can inherit one or more other protocols. This can be useful to add further requirements on top of the protocol. Doing so, the protocol that inherits other protocols would have to implement they’re methods and properties as well in the class/struct/enum. The syntax of protocol inheritance is similar to class inheritance as shown below

import Foundation

protocol TestProtocalA {
    var length: Int { get set}
}

protocol TestProtocalB {
    var breadth: Int { get set }
}

protocol TestProtocalC: TestProtocalA, TestProtocalB {
    var height: Int { get set }
}

class Volume: TestProtocalC {
    var height: Int = 5
    var breadth: Int = 10
    var length: Int = 2
    
    func printVolume() {
        print("Volume is \(height*breadth*length)")
    }
}

var v = Volume()
v.printVolume() //prints Volume is 100

To limit a protocol to act as class-only protocols and not be used with structs/enums, the following syntax is used.

protocol TestProtocalC: AnyObject, TestProtocalA, TestProtocalB {
    var height: Int {get set}
}

The above protocol can’t be used with structs and enums since it extends AnyObject.

Swift Protocol Compositions

Swift Protocols can be used as types inside a function too. We can combine multiple protocols into a single composition and use them as an argument inside a function as shown below. We can list as many protocols as you need, separating them with ampersands (&). A single class type can also be included within the composition which is generally used to specify superclass.

import Foundation

protocol TestProtocalA {
    var length: Int { get set }
}

protocol TestProtocalB {
    var breadth: Int { get set }
}

protocol TestProtocalC {
    var height: Int { get set }
}

struct Volume: TestProtocalA, TestProtocalB, TestProtocalC {
    
    var length: Int
    var breadth: Int
    var height: Int
    
}

func printVolume(to volume: TestProtocalA & TestProtocalB & TestProtocalC) {
    print("Volume is \(volume.length*volume.breadth*volume.height)")
}
var v = Volume(length : 5, breadth: 5, height: 5)
printVolume(to: v) //prints Volume is 125

Mixing Protocol and Classes

From Swift 4 we can now combine protocols as types with classes too as shown below.

protocol Name { }
protocol Age { }
class Zodiac { }
class Description { }

typealias Z = Name & Age & Zodiac
typealias D = Name & Age & Description

var nameAgeDescription: D
var nameAgeZodiac : Z

Adding a class to the protocol composition saves us from creating different subclasses for different types of classes from the above protocols and base classes.

Swift protocol optional

We can specify a property or method inside a protocol as optional. These requirements do not have to be implemented by types that conform to the protocol. To achieve this we need to specify the keyword @objc before the keyword protocol and add the keyword @objc optional for each optional properties/method as shown below.

import Foundation

@objc protocol OptionalPro {
    
    @objc optional var name: String { get set }
    @objc optional func printName()
    func helloWord()
    
}

class ClassOptional: OptionalPro {
    func helloWord() {
        print("hello world")
    } //Compiles fine without the need of implementing the optional requirements.
}