Swift Protocols

A protocol defines a set of rules — properties and methods — that a type must follow. It does not provide the actual code; it only lists what is required. Any struct, class, or enum that adopts a protocol must implement all its requirements.

The Job Contract Analogy


┌──────────────────────────────────────────────────┐
│  Protocol = A Job Contract                       │
│                                                  │
│  Contract says:                                  │
│  - Must clock in daily        (property)         │
│  - Must complete reports      (method)           │
│  - Must attend weekly meeting (method)           │
│                                                  │
│  Every employee who signs the contract           │
│  must do all three things.                       │
│  How they do it is their own choice.             │
└──────────────────────────────────────────────────┘

Defining a Protocol

protocol Greetable {
    var name: String { get }
    func greet() -> String
}

{ get } means the property must be readable. { get set } means it must also be writable. Methods have no body — just their signature.

Adopting a Protocol in a Struct

struct Person: Greetable {
    var name: String

    func greet() -> String {
        return "Hi, I am \(name)!"
    }
}

let p = Person(name: "Ananya")
print(p.greet())   // Hi, I am Ananya!

Adopting a Protocol in a Class

class Robot: Greetable {
    var name: String

    init(name: String) {
        self.name = name
    }

    func greet() -> String {
        return "BEEP. I am \(name)."
    }
}

let r = Robot(name: "R2D2")
print(r.greet())   // BEEP. I am R2D2.

Both Person and Robot fulfill the same contract but implement it differently. This is the power of protocols.

Multiple Protocol Adoption

protocol Flyable {
    func fly()
}

protocol Swimmable {
    func swim()
}

struct Duck: Flyable, Swimmable {
    func fly()  { print("Duck flies.") }
    func swim() { print("Duck swims.") }
}

A type can adopt multiple protocols separated by commas. This is how Swift achieves flexibility without the complexity of multiple inheritance.

Protocol as a Type

func makeNoise(for creature: Greetable) {
    print(creature.greet())
}

makeNoise(for: Person(name: "Ravi"))   // Hi, I am Ravi!
makeNoise(for: Robot(name: "WALL-E")) // BEEP. I am WALL-E.

You can use a protocol as a parameter type. The function works with any type that conforms to Greetable — it does not care whether it is a struct, class, or enum.

Protocol Flow Diagram


┌──────────────────────────────────────────────────────┐
│  Protocol: Greetable                                 │
│  Requires: name (property), greet() (method)         │
│                  │                                   │
│      ┌───────────┼───────────┐                       │
│   Person       Robot       Employee                  │
│   (struct)    (class)      (struct)                  │
│                                                      │
│  All three implement their own version of greet()    │
│  All three can be used wherever Greetable is needed  │
└──────────────────────────────────────────────────────┘

Protocol with Default Implementation (Extensions)

protocol Describable {
    var title: String { get }
    func describe()
}

extension Describable {
    func describe() {
        print("This is: \(title)")
    }
}

struct Book: Describable {
    var title: String
    // describe() is NOT required — default is used
}

let b = Book(title: "Swift Essentials")
b.describe()   // This is: Swift Essentials

Protocol extensions let you provide a default implementation. Types that adopt the protocol get it for free, but can override it if they need different behaviour.

Commonly Used Swift Protocols

ProtocolPurpose
EquatableCompare with ==
ComparableCompare with <, >
CodableEncode/decode to JSON
HashableUse in sets and as dict keys
CustomStringConvertibleCustom print output

Leave a Comment

Your email address will not be published. Required fields are marked *