Protocol oriented programming in Swift 5 - Basics
Don’t start with a class, start with a protocol.
– Apple
About POP
- Apple introduced the POP concept in Swift 2.0 (WWDC 2015).
- Other programming languages such as Java, PHP use the keyword “interface”.
What is a Protocol
- A protocol defines the method, property requirements that need to be implemented by conforming types i.e [Class, Structure and Enum].
- The protocol only provides a blueprint for the requirements and It can’t be instantiated.
POP Approach
- Start defining requirements in a Protocol.
- Advanced features supported by Protocols are:
- Protocol Inheritance.
- Protocol Composition.
- Protocol Extensions.
- Value-type preferred over Reference-type. Use struct, enum, and tuple instead of Class exclusively.
Protocol Naming
Refer: Swift API Design Guidelines
- Protocol Name should be in UpperCamelCase.
- Protocols that describe what something is should read as nouns (e.g. Collection).
- Protocols that describe a capability should be named using the suffixes able, ible, or ing (e.g. Equatable, ProgressReporting, CustomStringConvertible).
Protocol Example
1
2
3
protocol BaseType {
var objId: String { get set }
}
Note: Property in protocol must have explicit { get } or { get set } specifier
Create a Class-bound Protocol
1
2
3
protocol BaseType: AnyObject {
var objectId: String { get set }
}
Conforming to a protocol
1
2
3
struct Box: BaseType {
var objectId: String
}
Tip: Use auto-completion to add protocol stubs.
Conforming to protocols via extensions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct StudentModel {
var name: String
var rollNumber: Int
}
protocol StudentIdentifiable {
func studentDesc() -> String
}
extension StudentModel: StudentIdentifiable {
func studentDesc() -> String {
return "Student name is \(name) and his roll no. is \(rollNumber)"
}
}
let student1 = StudentModel(name: "Avadhesh", rollNumber: 1)
print(student1.studentDesc())
Polymorphism using protocols
polymorphism is the provision of a single interface to entities of different types.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
protocol Shape {
var area: Double { get }
var perimeter: Double { get }
}
struct Circle: Shape {
let radius: Double
var area: Double {
return .pi * radius * radius
}
var perimeter: Double {
return 2 * .pi * radius
}
}
struct Rectangle: Shape {
let width: Double
let height: Double
var area: Double {
return width * height
}
var perimeter: Double {
return 2 * (width + height)
}
}
struct Square: Shape {
let lengthOfSide: Double
var area: Double {
return lengthOfSide * lengthOfSide
}
var perimeter: Double {
return 4 * lengthOfSide
}
}
var arrayOfShapes = Array<Shape>()
arrayOfShapes.append(Square(lengthOfSide: 10))
arrayOfShapes.append(Rectangle(width: 30, height: 20))
arrayOfShapes.append(Circle(radius: 10))
for shape in arrayOfShapes {
print("Shape is \(type(of: shape)) of area \(shape.area) and permimeter \(shape.perimeter)")
}
Output:
1
2
3
4
5
Shape is Square of area 100.0 and permimeter 40.0
Shape is Rectangle of area 600.0 and permimeter 100.0
Shape is Circle of area 314.1592653589793 and permimeter 62.83185307179586
Protocol Inheritance
A protocol can be inherited from one or more protocol and the inherited one can have their own requirements on top of it.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
protocol Identifiable {
var eventType: String { get }
}
protocol Describing: Identifiable {
var desc: String { get }
}
protocol EventRecording: Describing {}
struct Event: EventRecording,CustomStringConvertible {
var eventType: String
var desc: String
var description: String {
return "\(eventType) : \(desc)"
}
}
let homeEvent = Event(eventType: "HomePage", desc: "Logged In Succesfully!!!")
print(homeEvent)
Output:
1
HomePage : Logged In Succesfully!!!
Protocol Composition
Protocol composition lets the type adopt multiple protocols. It allows us to break requirements into multiple protocols instead of inheriting requirements from a single protocol or super-class.
Consider this class hierarchy :
Note: Flowchart created with Creately
With Protocol composition, we can have our requirements specified under protocols for Employee, Senior, and Junior. This approach allows us to only adopt to the requirement we desire.
1
struct JuniorJavaDeveloper: EmployeeProtocol, JuniorProtocol { }