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.
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
}
- 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 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
-
- 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}
).
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
}
-
- 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.
- A {get set} computed property is readable as well as writable. For a {get}, it need not be settable.
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.
}
- Functions 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.
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