Swift - Protocols



Protocols provide a blueprint for Methods, properties and other requirements functionality. It is just described as a methods or properties skeleton instead of an implementation. Methods and properties implementation can further be done by defining classes, functions and enumerations. Conformance of a protocol is defined as the methods or properties satisfying the requirements of the protocol.

Defining Protocols in Swift

In Swift, the definition of the protocol is quite similar to the class, structure or enumeration. A protocol is defined using the protocol keyword.

Syntax

Following is the syntax for the protocol −

protocol SomeProtocol {
   // protocol definition 
}

Protocols are declared after the class, structure or enumeration type names. Single and multiple protocol declarations are also possible. If multiple protocols are defined they have to be separated by commas.

struct SomeStructure: Protocol1, Protocol2 {
   // structure definition 
}

When a protocol has to be defined for a superclass, the protocol name should follow the superclass name with a comma.

class SomeClass: SomeSuperclass, Protocol1, Protocol2 {
   // class definition 
}

Property, Method and Initialization Requirements

A protocol requires any conforming type which can provide property, method and initialization.

Property Requirements − A protocol is used to specify a particular class type property or instance property. It only specifies the type or instance property rather than specifying whether it is a stored or computed property. Also, specify whether the property is 'gettable' or 'settable'.

Property requirements are declared by 'var' keyword as property variables. {get set} is used to declare gettable and settable properties after their type declaration. Gettable is mentioned by {get} property after their type declaration.

Syntax

The following syntax is for defining properties in protocol −

protocol SomeProtocol {
   var propertyName : Int {get set}
}

Method Requirements − A protocol is used to specify particular type methods or instance methods. It only contains the definition part without curly braces and body. It allows methods to have variadic parameters.

Syntax

The following syntax is for defining methods in protocol −

protocol SomeProtocol {
   func methodName(parameters)
}

Mutating Method Requirements − If you want to specify a mutating method in the protocol then use the mutating keyword before the definition of the method. It allows structure and enumeration to adopt the protocol with the mutating method.

Syntax

The following syntax is for defining mutating methods in protocol −

protocol SomeProtocol {
   mutating func methodName(parameters)
}

Initializer Requirements: A protocol is also used to specify an initializer that will implemented by conforming types. It only contains the definition part just like the normal initializers, but without and curly braces and a body. We can specify either designated or convenience initializers.

Also, the class or structure that conforms to the protocol must use the required modifier before the implementation of the initializer. Protocol conformance is ensured on all subclasses for explicit or inherited implementation by 'required' modifier. When a subclass overrides its superclass initialization requirement it is specified by the 'override' modifier keyword.

Syntax

The following syntax is for defining initializers in protocol −

protocol SomeProtocol {
  init(parameters)
}

Example

Swift program to create a protocol that conforms by a class.

// Protocol
protocol classa {

   // Properties
   var marks: Int { get set }
   var result: Bool { get }
   
   // Method
   func attendance() -> String
   func markssecured() -> String
}

// Protocol
protocol classb: classa {
   
   // Properties
   var present: Bool { get set }
   var subject: String { get set }
   var stname: String { get set }
}

// Class that conform Protocol
class classc: classb {
   var marks = 96
   let result = true
   var present = false
   var subject = "Swift 4 Protocols"
   var stname = "Protocols"
   
   func attendance() -> String {
      return "The \(stname) has secured 99% attendance"
   }
   
   func markssecured() -> String {
      return "\(stname) has scored \(marks)"
   }
}

// Instance of class
let studdet = classc()
studdet.stname = "Swift 4"
studdet.marks = 98

// Accessing methods and properties
print(studdet.markssecured())

print(studdet.marks)
print(studdet.result)
print(studdet.present)
print(studdet.subject)
print(studdet.stname)

Output

It will produce the following output −

Swift 4 has scored 98
98
true
false
Swift 4 Protocols
Swift 4

Example

Swift program to create a protocol with mutating method requirements.

// Protocol
protocol daysofaweek {
   // Mutating method
   mutating func display()
}

// Enumeration that conforms to the Protocol
enum days: daysofaweek {
   case sun, mon, tue, wed, thurs, fri, sat
    
   mutating func display() {
      switch self {
         case .sun:
            print("Sunday")
         case .mon:
            print("Monday")
         case .tue:
            print("Tuesday")
         case .wed:
            print("Wednesday")
         case .thurs:
            print("Thursday")
         case .fri:
            print("Friday")
         case .sat:
            print("Saturday")
      }
   }
}

// Instance of enumeration
var res = days.wed
res.display()

Output

It will produce the following output −

Wednesday

Example

Swift program to create a protocol with an initializer that conforms to the class.

// Protocol
protocol tcpprotocol {

   // Initializer
   init(no1: Int)
}

class mainClass {
   var no1: Int // local storage
   init(no1: Int) {
      self.no1 = no1 // initialization
   }
}

// Class that conform protocol 
class subClass: mainClass, tcpprotocol {
   var no2: Int
   init(no1: Int, no2 : Int) {
      self.no2 = no2
      super.init(no1:no1)
   }
   
   // Requires only one parameter for convenient method
   required override convenience init(no1: Int)  {
      self.init(no1:no1, no2:0)
   }
}

// Class instances
let obj1 = mainClass(no1: 20)
let obj2 = subClass(no1: 30, no2: 50)

print("res is: \(obj1.no1)")
print("res is: \(obj2.no1)")
print("res is: \(obj2.no2)")

Output

It will produce the following output −

res is: 20
res is: 30
res is: 50

Protocols as Types

Instead of implementing functionalities in a protocol they are used as types for functions, classes, methods etc. Protocols can be accessed as types in:

  • Function, method or initialize as a parameter or return type

  • Constant, variable or property

  • Arrays, dictionaries or other containers as items

Example

protocol Generator {
   associatedtype Element
   mutating func next() -> Element?
}

extension Array: Generator {
   mutating func next() -> Element? {
      guard !isEmpty else { return nil }
      return removeFirst()
   }
}

var items = [10, 20, 30]
while let x = items.next() {
   print(x)
}

for lists in [1, 2, 3].compactMap({ i in i * 5 }) {
   print(lists)
}

print([100, 200, 300])
print([1, 2, 3].map({ i in i * 10 }))

Output

It will produce the following output −

10
20
30
5
10
15
[100, 200, 300]
[10, 20, 30]

Adding Protocol Conformance with an Extension

Existing types can be adopted and conformed to a new protocol by making use of extensions. New properties, methods and subscripts can be added to existing types with the help of extensions.

Example

protocol AgeClassificationProtocol {
   var age: Int { get }
   func ageType() -> String
}

class Person: AgeClassificationProtocol {
   let firstname: String
   let lastname: String
   var age: Int

   init(firstname: String, lastname: String, age: Int) {
      self.firstname = firstname
      self.lastname = lastname
      self.age = age
   }

   func fullname() -> String {
      return firstname + " " + lastname
   }

   func ageType() -> String {
      switch age {
         case 0...2:
            return "Baby"
         case 3...12:
            return "Child"
         case 13...19:
            return "Teenager"
         case 65...:
            return "Elderly"
         default:
            return "Normal"
        }
    }
}

let obj = Person(firstname: "Mona", lastname: "Singh", age: 10)
print("Full Name: \(obj.fullname())")
print("Age Type: \(obj.ageType())")

Output

It will produce the following output −

Full Name: Mona Singh
Age Type: Child

Protocol Inheritance

Swift allows protocols to inherit properties from its defined properties. It is similar to that of class inheritance but with the choice of listing multiple inherited protocols separated by commas. With the help of protocol, we can achieve multiple inheritance that we cannot achieve by using classes.

Syntax

The following syntax is for protocol inheritance −

protocol SomeProtocol: protocol1, protocol2 {
  // statement
}

Example

protocol ClassA {
   var no1: Int { get set }
   func calc(sum: Int)
}

protocol Result {
   func print(target: ClassA)
}

class Student2: Result {
   func print(target: ClassA) {
      target.calc(sum: 1)
   }
}

class ClassB: Result {
   func print(target: ClassA) {
      target.calc(sum: 5)
   }
}

class Student: ClassA {
   var no1: Int = 10
    
   func calc(sum: Int) {
      no1 -= sum
      print("Student attempted \(sum) times to pass")
        
      if no1 <= 0 {
         print("Student is absent for the exam")
      }
   }
}

class Player {
   var stmark: Result!
    
   init(stmark: Result) {
      self.stmark = stmark
   }
    
   func print(target: ClassA) {
      stmark.print(target: target)
   }
}

var marks = Player(stmark: Student2())
var marksec = Student()

marks.print(target: marksec)
marks.print(target: marksec)
marks.print(target: marksec)

marks.stmark = ClassB()
marks.print(target: marksec)
marks.print(target: marksec)
marks.print(target: marksec)

Output

It will produce the following output −

Student attempted 1 times to pass
Student attempted 1 times to pass
Student attempted 1 times to pass
Student attempted 5 times to pass
Student attempted 5 times to pass
Student is absent for exam
Student attempted 5 times to pass
Student is absent for exam

Class Only Protocols

When protocols are defined and the user wants to define protocol with classes it should be added by defining class first followed by the protocol's inheritance list.

Example

protocol tcpprotocol {
   init(no1: Int)
}

class mainClass {
   var no1: Int // local storage
   init(no1: Int) {
      self.no1 = no1 // initialization
   }
}

class subClass: mainClass, tcpprotocol {
   var no2: Int
   init(no1: Int, no2 : Int) {
      self.no2 = no2
      super.init(no1:no1)
   }
   
   // Requires only one parameter for convenient method
   required override convenience init(no1: Int)  {
      self.init(no1:no1, no2:0)
   }
}

let res = mainClass(no1: 20)
let obj = subClass(no1: 30, no2: 50)

print("res is: \(res.no1)")
print("res is: \(obj.no1)")
print("res is: \(obj.no2)")

Output

It will produce the following output −

res is: 20
res is: 30
res is: 50

Protocol Composition

Swift allows multiple protocols to be called at once with the help of protocol composition. We are allowed to combine multiple protocols in a single requirement with the help of protocol composition. It does not define any new protocol types and only uses the existing ones.

In protocol composition, we can specify as many as protocol we want where each protocol is separated by ampersands(&). It can also contain one class type that we can use to specify the superclass.

Syntax

protocol<SomeProtocol & AnotherProtocol>

Example

protocol StName {
   var name: String { get }
}

protocol Stage {
   var age: Int { get }
}

struct Person: StName, Stage {
   var name: String
   var age: Int
}

// Protocol Composition
func printCelebrator(celebrator: StName & Stage) {
   print("\(celebrator.name) is \(celebrator.age) years old")
}

let studName = Person(name: "Priya", age: 21)
printCelebrator(celebrator: studName)

let stud = Person(name: "Rehan", age: 29)
printCelebrator(celebrator: stud)

let student = Person(name: "Roshan", age: 19)
printCelebrator(celebrator: student)

Output

It will produce the following output −

Priya is 21 years old
Rehan is 29 years old
Roshan is 19 years old

Checking for Protocol Conformance

Protocol conformance is tested by 'is' and 'as' operators similar to that of type casting.

  • The is operator returns true if an instance conforms to protocol standard and returns false if it fails.

  • The as? version of the downcast operator returns an optional value of the protocol's type, and this value is nil if the instance does not conform to that protocol.

  • The as version of the downcast operator forces the downcast to the protocol type and triggers a runtime error if the downcast does not succeed.

Example

import Foundation
@objc protocol rectangle {
   var area: Double { get }
}

@objc class Circle: rectangle {
   let pi = 3.1415927
   var radius: Double
   var area: Double { return pi * radius * radius }
   init(radius: Double) { self.radius = radius }
}

@objc class result: rectangle {
   var area: Double
   init(area: Double) { self.area = area }
}

class sides {
   var rectsides: Int
   init(rectsides: Int) { self.rectsides = rectsides }
}
let objects: [AnyObject] = [Circle(radius: 2.0),result(area: 198),sides(rectsides: 4)]

for object in objects {
   if let objectWithArea = object as? rectangle {
      print("Area is \(objectWithArea.area)")
   } else {
      print("Rectangle area is not defined")
   }
}

Output

It will produce the following output −

Area is 12.5663708
Area is 198.0
Rectangle area is not defined
Advertisements