Swift Protocol With Examples

Continuing with our series of Swift tutorials, we’ll be discussing and playing around with another important element namely Swift Protocol in this tutorial. Furthemore we’ll be looking at the changes that Swift 4 brings in.

Swift Protocol

Here’s how the Apple docs define a protocol.

swift-protocol

If the above definition didn’t make complete sense to you here’s a simpler one.

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

In other words, to become THAT, you need to use THIS.

Swift Protocol Syntax

The syntax of Swift Protocol is similar to Classes, Structures and Enums.


protocol MyFirstProtocol{
    //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 A : MyFirstProtocol{
    //Enter your code
}
struct B : MyFirstProtocol{
    //Enter your code
}
enum E : MyFirstProtocol {
    //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 FirstProtocol{
    //Properties and methods declarations goes in here.
}
protocol SecondProtocol{
    //Properties and methods declarations goes in here.
}
class S {
    //Let's say this is the super class.
}
class A : S,FirstProtocol, SecondProtocol{
    //Enter your code
}
struct B : FirstProtocol, SecondProtocol{
    //Enter your code
}
enum E : FirstProtocol, SecondProtocol {
    //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}).
      Following are the two ways to do so.

protocol FirstProtocol{
    var age : Int {get set} //read-write property
    var year : Int {get}  //optional write property
}
    1. 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 A : FirstProtocol{
    var age = 23
    var year = 2017
}
var a = A()
a.year = 2018
print(a.year) //prints 2018
//Alternative case
class A : FirstProtocol{
    var age = 23
    let year = 2017 //now this is a constant.
}
  1. Functions 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.


protocol CSDegree{
    var cgpa : Double {get set}
    var name : String? {get set}
    var coursesCompleted : Bool? {get set}
    func haveYouCompletedOperatingSystems() -> Bool
    func haveYouCompletedJavaCourse() -> Bool
    func printAllGrades(grade: String...)
    func degreeGranted()
    mutating func hackToGetMyDegree()
}
class Student : CSDegree {
    var cgpa: Double = 2
    var name: String?
    var coursesCompleted: Bool?
    func haveYouCompletedJavaCourse() -> 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 haveYouCompletedJavaCourse() && haveYouCompletedOperatingSystems() && cgpa > 4
        {
            print("Congrats, you've completed Bachelors in Computer Science")
        }
        else{
            print("Sorry, you won't be granted your degree this year")
        }
    }
    func hackToGetMyDegree() {
        cgpa = 8.5
        coursesCompleted = true
    }
}
var s = Student()
s.cgpa = 6.93
s.name = "Anupam"
s.coursesCompleted = false
print(s.name ?? "Not found") //prints "Anupamn"
s.haveYouCompletedOperatingSystems()
s.haveYouCompletedJavaCourse()
s.degreeGranted() //Sorry, you won't be granted your degree this year
s.printAllGrades(grade: "A","D") //prints Grades of Anupam 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() //prints Congrats, you've completed Bachelors in Computer Science

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


struct StudentStruct : CSDegree {
    var cgpa: Double = 8.5
    var name: String?
    var coursesCompleted: Bool?
    func haveYouCompletedJavaCourse() -> 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 haveYouCompletedJavaCourse() && haveYouCompletedOperatingSystems() && cgpa > 4
        {
            print("Congrats, you've completed Bachelors in Computer Science")
        }
        else{
            print("Sorry, you won't be granted your degree this year")
        }
    }
    mutating func hackToGetMyDegree() {
        cgpa = 8.5
        coursesCompleted = true
    }
}
var s1 = StudentStruct()
s1.cgpa = 3.9
s1.name = "Anupam"
s1.coursesCompleted = false
print(s1.name ?? "Not found")
s1.haveYouCompletedOperatingSystems()
s1.haveYouCompletedJavaCourse()
s1.degreeGranted()
s1.printAllGrades(grade: "A","D")
s1.hackToGetMyDegree()
s1.degreeGranted()

Swift Protocols With Intializers

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:


protocol MyInitialiser {
    init(name: String)
}
class TestInitProtocol : MyInitialiser{
    required init(name: String) {
        print("Welcome to (name)")
    }
}
var t = TestInitProtocol(name: "JournalDev") //prints Welcome to JournalDevn"
//Convenience initializer example
protocol MyInitialiser {
    init(name: String)
}
class TestInitProtocol : MyInitialiser{
    convenience required init(name: String) {
        print("Welcome to (name)")
        self.init()
    }
}
var t = TestInitProtocol(name: "JournalDev")

Using Protocol Initialisers along with initialisers from SuperClass


protocol MyInitialiser {
    init()
}
class SuperClass{
    init() {
    }
}
class TestInitProtocol : SuperClass, MyInitialiser{
        required override init() {
            print("required from protocol, override from superclass")
    }
}
var t = TestInitProtocol()

Using Swift Protocols With Extensions

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


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 = "Anupam"
print(n.str) //prints "Hello, Anupamn"

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.


protocol WebsiteName{
    var name : String {get}
}
extension WebsiteName{
    var name : String {
        return "JournalDev"
    }
}
class W : WebsiteName {
}
var w = W()
print(w.name)

Swift Protocols with Enums

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


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 "Wednesdayn"
day.firstDayOfTheWeek()
print(day.currentDay()) //prints "Sundayn"

Inheritance in Swift Protocols

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


protocol A {
    var length : Int {get set}
}
protocol B {
    var breadth : Int {get set}
}
protocol C : A, B {
    var height : Int {get set}
}
class Volume : C {
    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 100

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


protocol C : AnyObject, A, B {
    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.


protocol A {
    var length : Int {get set}
}
protocol B {
    var breadth : Int {get set}
}
protocol C {
    var height : Int {get set}
}
struct Volume : A, B, C {
    var length: Int
    var breadth: Int
    var height: Int
}
func printVolume(to volume: A & B & C) {
    print("Volume is (volume.length*volume.breadth*volume.height)")
}
var v = Volume(length : 5, breadth: 5, height: 5)
printVolume(to: v) //prints 125

Swift 4 Mixing Protocol and Classes

With 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.

Optional Protocol Requirements

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/methods as shown below.


@objc protocol OptionalPro{
    @objc optional var name : String{get set}
    @objc optional func printName()
    func helloWord()
}
class O : OptionalPro{
    func helloWord() {
        print("hello world")
    }
    //Compiles fine without the need of implementing the optional requirements.
}

This brings an end to this tutorial on Swift Protocol.

Reference: Apple Documentation

By admin

Leave a Reply

%d bloggers like this: