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 }
- If there are more than one protocols, they’re separated by commas.
- 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
- A protocol can contain properties as well as methods that the class/struct/enum conforming to the protocol would implement.
- First things first, variables inside a protocol must be declared as a variable and not a constant(let isn’t allowed).
- Properties inside a protocol must contain the keyword var.
- 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
- Functions(Swift Function) inside a protocol are defined without a body.
- Default values within functions aren’t allowed.
- Functions with variadic parameters are also allowed.
- Mutating functions can be defined inside a keyword by adding the keyword mutating.
- We do not need to write the mutating keyword when writing an implementation of that method for a class.
- 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. }