iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Protocols

A Swift protocol is a contract — required methods, properties, associated types. Conform with extension Type: Protocol. Protocol extensions add default implementations. Generic constraints unlock zero-cost abstractions.

Define, conform, extension, associated

EXAMPLE
// 1) Define a protocol
protocol Describable {
    var description: String { get }
}

// 2) Conform — by extension
struct User { let name: String }

extension User: Describable {
    var description: String { "User: \\(name)" }
}

// 3) Protocol with default implementation
protocol Greetable {
    var name: String { get }
    func greet() -> String
}

extension Greetable {
    func greet() -> String { "Hi, I'm \\(name)" }
}

struct Robot: Greetable { let name: String }
print(Robot(name: "R2").greet())   // uses the default

// 4) Composition — conform to multiple
protocol Identifiable2 {
    associatedtype ID: Hashable
    var id: ID { get }
}

protocol Timestamped {
    var createdAt: Date { get }
}

struct Post: Identifiable2, Timestamped {
    let id: UUID
    let createdAt: Date
    let title: String
}

// 5) Generic constraints
func printAll<T: Describable>(_ items: [T]) {
    items.forEach { print($0.description) }
}

// 6) Existential — `any Protocol` (boxed)
func printAny(_ items: [any Describable]) {
    items.forEach { print($0.description) }
}
// Existential = heterogenous array; generic = monomorphised at compile-time.

// 7) Constrained existentials (Swift 5.7+)
func print(_ items: any Collection<String>) { /* … */ }

// 8) Associated types — generic protocols
protocol Storage {
    associatedtype Item
    func add(_ item: Item)
    func all() -> [Item]
}

struct UserStore: Storage {
    typealias Item = User
    var users: [User] = []
    mutating func add(_ item: User) { users.append(item) }
    func all() -> [User] { users }
}

// 9) Protocol witness — manual dispatch (testable mocks)
struct AnalyticsClient {
    var track: (String, [String: Any]) -> Void
}

extension AnalyticsClient {
    static var live: Self {
        .init { event, props in /* send to Mixpanel */ }
    }
    static var mock: Self {
        .init { event, props in print("mock: \\(event) \\(props)") }
    }
}

// 10) Equatable, Hashable, Codable — auto-conformance when all fields conform
struct Money: Equatable, Hashable, Codable {
    let amount: Decimal
    let currency: String
}

// 11) CustomStringConvertible — printable
struct Tag: CustomStringConvertible {
    let value: String
    var description: String { "#\\(value)" }
}
print(Tag(value: "swift"))   // "#swift"

// 12) Where clauses — constrain associated types
func sum<T: Sequence>(_ s: T) -> Int where T.Element == Int {
    s.reduce(0, +)
}

extension Collection where Element == String {
    func longest() -> String? { self.max(by: { $0.count < $1.count }) }
}

// 13) Result-builder + protocol — SwiftUI-style declarative views
protocol View2 { var body: String { get } }
struct Text2: View2 { let s: String; var body: String { s } }
struct Row2: View2 { let items: [String]; var body: String { items.joined(separator: " ") } }

// 14) Sealed-ish hierarchy with protocols + final classes / structs
protocol Animal { func sound() -> String }
final class Dog: Animal { func sound() -> String { "woof" } }
final class Cat: Animal { func sound() -> String { "meow" } }

// 15) Common pitfalls
//   • Forgetting `mutating` on a method in a value type that conforms to a protocol
//   • Self requirements (Self: Equatable) → can't be used as `any Equatable` (until newer Swift)
//   • Overriding default implementations — order: extension > type-specific > default
//   • Existential overhead — `any P` boxes values; prefer generic for hot paths

Why it matters

Protocol + extension is Swift’s favourite composition tool. Default implementations replace inheritance; generic constraints monomorphise at compile time — abstraction without runtime cost.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
protocol Greet {
    func hello() -> String
}
struct User: Greet {
    let name: String
    func hello() -> String { "hi, \(name)" }
}
Try it Yourself »

Exercise

Adopt a protocol.

struct User: { func hello() -> String { "hi" } }

Test yourself

Q1. A protocol is most similar to…
Q2. Add default behaviour to a protocol with…
Q3. Protocols + extensions enable…

Discussion

Loading…