Swift Protocols

A protocol is a contract. It lists the properties and methods that any conforming type must provide — but it does not implement them. Think of a protocol like a job description: it says what skills are required, but not how the employee performs the job. Any struct, class, or enum can sign that contract by implementing what is required.

Defining a Protocol

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

The { get } after a property means the conforming type must provide at least a getter. Writing { get set } means it must also be writable.

Conforming to a Protocol

struct Person: Greetable {
    var name: String

    func greet() -> String {
        return "Hello, I'm \(name)!"
    }
}

class Robot: Greetable {
    var name: String

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

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

let p = Person(name: "Alice")
let r = Robot(name: "R2D2")

print(p.greet())   // Output: Hello, I'm Alice!
print(r.greet())   // Output: BEEP. I am R2D2.

Diagram: Protocol as a Contract

    protocol Greetable
    +---------------------+
    | var name: String    |
    | func greet()        |
    +---------------------+
         |           |
  struct Person   class Robot
  (must provide   (must provide
   name + greet)   name + greet)

Multiple Protocol Conformance

A type can conform to more than one protocol simultaneously.

protocol Printable {
    func printInfo()
}

protocol Saveable {
    func save()
}

struct Document: Printable, Saveable {
    var title: String

    func printInfo() {
        print("Document: \(title)")
    }

    func save() {
        print("Saving \(title) to disk...")
    }
}

let doc = Document(title: "Swift Guide")
doc.printInfo()   // Output: Document: Swift Guide
doc.save()        // Output: Saving Swift Guide to disk...

Protocol as a Type

You can use a protocol name as a type. Any conforming type can fill that variable.

let greeters: [Greetable] = [Person(name: "Alice"), Robot(name: "R2D2")]

for greeter in greeters {
    print(greeter.greet())
}
// Output:
// Hello, I'm Alice!
// BEEP. I am R2D2.

Protocol with Default Implementation

Protocol extensions add default behavior that all conforming types receive automatically. A type can still override the default if it needs different behavior.

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

extension Describable {
    func describe() {
        print(description)   // Default implementation
    }
}

struct Product: Describable {
    var description: String
}

let phone = Product(description: "Smartphone — 128GB storage")
phone.describe()
// Output: Smartphone — 128GB storage

Protocol Inheritance

Protocols can inherit from other protocols, building a hierarchy of requirements.

protocol Named {
    var name: String { get }
}

protocol Aged: Named {
    var age: Int { get }
}

struct Employee: Aged {
    var name: String
    var age: Int
}

let emp = Employee(name: "Bob", age: 35)
print("\(emp.name), age \(emp.age)")
// Output: Bob, age 35

Equatable and Comparable Protocols

Swift's standard library is built on protocols. Two widely used ones are Equatable (enables ==) and Comparable (enables <, >).

struct Point: Equatable {
    var x: Int
    var y: Int
}

let p1 = Point(x: 1, y: 2)
let p2 = Point(x: 1, y: 2)
let p3 = Point(x: 5, y: 8)

print(p1 == p2)   // Output: true
print(p1 == p3)   // Output: false

Codable Protocol

Codable is a combination of Encodable and Decodable. Conforming types can be converted to and from JSON automatically — a common need in apps that talk to web APIs.

struct User: Codable {
    var name: String
    var age: Int
}

let user = User(name: "Alice", age: 30)
let encoded = try! JSONEncoder().encode(user)
let json = String(data: encoded, encoding: .utf8)!
print(json)
// Output: {"name":"Alice","age":30}

let decoded = try! JSONDecoder().decode(User.self, from: encoded)
print(decoded.name)   // Output: Alice

Summary

Protocols define requirements without implementations. Any struct, class, or enum can conform by supplying what the protocol asks for. Protocol extensions add shared default implementations. Use protocols to write flexible, reusable code that works with any conforming type — not just a specific class or struct.

Leave a Comment

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