Protocols: defining a contractswift-6.4/ios-26
Lesson 1 / 5
Unit 4 · Protocols & generics

Protocols: defining a contract

Coming from
Note

Unit 4 · Protocols & generics. This is the abstraction unit — how Swift lets many types share a shape without sharing a base class. It underpins the whole standard library.

A protocol is a contract. It lists requirements — properties, methods — that a conforming type promises to provide, and the compiler enforces the promise. Protocols are how Swift says "any type that can do X," which turns out to be a more flexible tool than inheritance for almost everything you'll model.

Declaring a protocol

protocol Vehicle {
    var wheelCount: Int { get }     // a readable property
    var name: String { get set }    // readable and writable
    func describe() -> String       // a method requirement
}

A property requirement states its access with { get } (readable) or { get set } (readable and writable). A method requirement is just a signature — no body. The protocol says what, never how.

Conforming to it

struct Bicycle: Vehicle {
    let wheelCount = 2          // a `let` satisfies a `{ get }` requirement
    var name: String

    func describe() -> String {
        "\(name) has \(wheelCount) wheels"
    }
}

Writing struct Bicycle: Vehicle declares the conformance; the compiler then checks that every requirement is met. Miss one and it won't build — the contract is verified at compile time, not hoped for at runtime. Note that a let wheelCount satisfies { get }, while a { get set } property needs a var.

Using a protocol as a type

The payoff: you can write code against the protocol and stay ignorant of the concrete type.

func report(_ vehicle: Vehicle) -> String {
    vehicle.describe()
}

let fleet: [Vehicle] = [Bicycle(name: "Cruiser"), Bicycle(name: "Tandem")]
for v in fleet { print(v.describe()) }

report accepts anything that conforms to Vehicle. An array typed [Vehicle] can hold any mix of concrete types, as long as each is a Vehicle. You program to the contract, and any conformer slots in.

Coming from Kotlin

A Swift protocol maps closely to a Kotlin interface. The differences you'll feel: Swift protocols can require properties (not just methods), can be adopted by value types (struct/enum), and — as you'll see next lesson — can ship default implementations via extensions rather than needing an abstract base class.

Composing protocols with &

A parameter can require conformance to several protocols at once:

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

func greet(_ person: Named & Aged) -> String {
    "\(person.name), age \(person.age)"
}

Named & Aged is a protocol composition: it means "a value that is both." This keeps protocols small and single-purpose — you combine them at the point of use instead of building one giant protocol that demands everything.

Tip

Keep protocols small. A protocol should describe one capability. Named, Equatable, Identifiable each say one thing, and you compose them with & where a function needs more. Small protocols conform easily and read clearly; a kitchen-sink protocol is painful to adopt and hard to reuse.

Your turn

Define a conformance and use the protocol as a type.

Conform to a protocol
Edit the code on the right, then run the hidden tests.

Knowledge check

Q: What does { get } versus { get set } mean on a protocol property? { get } requires the property be readable — a let, a var, or a computed getter all satisfy it. { get set } requires it be readable and writable, so a var (stored or computed with a setter) is needed.

Q: What does Named & Aged express as a parameter type? Protocol composition: the value must conform to both Named and Aged. It lets you keep protocols small and combine capabilities only where a function actually needs them.

Solution.swiftSwift Testing
ConsoleReady · runs in a sandboxed Swift container
Press Run to compile against the hidden test suite.