thecodex.expert · The Codex Family of Knowledge
Swift

Protocols and Generics

Protocol-oriented programming is Swift's answer to class inheritance — more flexible, more composable, and usable with value types.

Swift 5.10+ Protocol-oriented Last verified:
Canonical Definition

Swift protocols define a contract of requirements (methods, properties, associated types) that any conforming type must satisfy; protocol extensions provide default implementations; generics parameterise functions and types over protocol-constrained types; some T is a compile-time opaque type with zero boxing cost; any T is a runtime existential with type-erasure boxing.

The central idea

Inheritance says "a Dog IS-A Animal." Protocols say "a Dog CAN bark, CAN fetch." A struct can conform to multiple protocols; it cannot have multiple superclasses. Swift leans heavily on protocol conformance — even the standard library is built on protocols like Equatable, Comparable, Hashable, Codable, and Identifiable.

Protocols: conformance and default implementations

Swiftprotocols.swift
protocol Greetable {
    var name: String { get }   // required read-only property
    func greet() -> String
}

// Protocol extension: default implementation
extension Greetable {
    func greet() -> String {
        return "Hello, I'm \(name)!"
    }
}

struct Person: Greetable {
    let name: String
    // greet() provided by default — no need to implement
}

struct Robot: Greetable {
    let name: String
    func greet() -> String {   // override the default
        return "GREETINGS. I AM \(name.uppercased())."
    }
}

// Protocol composition: &
func introduce(_ thing: Greetable & CustomStringConvertible) {
    print(thing.greet())
    print(thing.description)
}

let p = Person(name: "Ana")
print(p.greet())   // Hello, I'm Ana!
let r = Robot(name: "R2D2")
print(r.greet())   // GREETINGS. I AM R2D2.

Generics

Swiftgenerics.swift
// Generic function: T is constrained to Comparable
func maximum<T: Comparable>(_ a: T, _ b: T) -> T {
    return a > b ? a : b
}

print(maximum(3, 7))           // 7
print(maximum("apple", "banana"))  // banana

// Generic struct
struct Stack<Element> {
    private var items: [Element] = []
    mutating func push(_ item: Element) { items.append(item) }
    mutating func pop() -> Element? { items.popLast() }
    var top: Element? { items.last }
    var isEmpty: Bool { items.isEmpty }
}

var stack = Stack<Int>()
stack.push(1); stack.push(2); stack.push(3)
print(stack.pop()!)   // 3

// where clause: multiple constraints
func merge<T, U: Sequence>(_ dict: [T: [U.Element]], _ seq: U) -> [T: [U.Element]]
    where U.Element: Hashable {
    return dict   // simplified — just shows constraint syntax
}

Codable: automatic JSON serialisation

Swiftcodable.swift
import Foundation

// Codable = Encodable & Decodable — synthesised automatically for simple structs
struct User: Codable {
    let id: Int
    let name: String
    let email: String
    let isAdmin: Bool

    // Custom JSON keys via CodingKeys enum
    enum CodingKeys: String, CodingKey {
        case id, name, email
        case isAdmin = "is_admin"   // snake_case JSON -> camelCase Swift
    }
}

// Encoding: Swift -> JSON
let user = User(id: 1, name: "Alice", email: "alice@example.com", isAdmin: false)
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let jsonData = try! encoder.encode(user)
print(String(data: jsonData, encoding: .utf8)!)

// Decoding: JSON -> Swift
let json = """{"id":2,"name":"Bob","email":"bob@example.com","is_admin":true}"""
let decoder = JSONDecoder()
let decoded = try! decoder.decode(User.self, from: json.data(using: .utf8)!)
print(decoded.name)   // Bob
print(decoded.isAdmin)   // true

some vs any: opaque types and existentials

some Protocol is an opaque type — the function returns a single specific type conforming to the protocol, but hides which type. The caller can't inspect it, but the compiler knows and optimises accordingly (zero boxing). any Protocol is an existential type — a type-erased box that can hold any conforming type at runtime, at the cost of a heap allocation for large types. SwiftUI uses some View everywhere; use any when you genuinely need a heterogeneous collection.

Swiftsome_any.swift
protocol Shape {
    func area() -> Double
}

struct Circle: Shape {
    var radius: Double
    func area() -> Double { .pi * radius * radius }
}

struct Square: Shape {
    var side: Double
    func area() -> Double { side * side }
}

// some Shape: opaque — always returns the same (unknown to caller) concrete type
// Compiler can inline and optimise — zero overhead
func makeCircle(radius: Double) -> some Shape {
    return Circle(radius: radius)
}

// any Shape: existential — can hold different types at runtime
// Small overhead (indirection) — but enables heterogeneous collections
func totalArea(_ shapes: [any Shape]) -> Double {
    shapes.reduce(0) { $0 + $1.area() }
}

let shapes: [any Shape] = [Circle(radius: 5), Square(side: 3)]
print(totalArea(shapes))   // ~87.5
Commonly confused
Protocol conformance is explicit in Swift, not implicit like Go. Unlike Go (where any type with the right methods satisfies an interface), Swift requires explicit conformance: struct Dog: Animal { ... }. You cannot retroactively say a type satisfies a protocol you wrote later — you need a extension Dog: NewProtocol { ... } extension. This makes the conformance graph explicit and searchable.
Associated types make protocols non-directly usable as types. protocol Collection { associatedtype Element } cannot be used as var things: Collection — the associated type is unresolved. Use any Collection (existential, Swift 5.7+), a generic constrained to Collection, or a concrete type conforming to Collection. This is why some Collection and generics exist.
Protocol extensions can't override methods on concrete types. If both a protocol extension and a struct provide the same method, the struct's version always wins when the variable is typed as the concrete struct. If typed as the protocol, the protocol extension's version runs. This is static dispatch (protocol extension methods) vs dynamic dispatch (protocol requirements), and it can cause surprising behaviour if you're not aware of the distinction.

Protocol witness tables and the Swift ABI

When a type conforms to a protocol, the Swift compiler generates a protocol witness table (PWT) — a struct containing function pointers for each protocol requirement. When calling a protocol method through an existential (any Protocol), Swift uses the PWT for dynamic dispatch. Each type has its own PWT for each protocol it conforms to. For generics (func foo<T: Protocol>(x: T)), the compiler can specialise the function for each concrete type (like C++ template instantiation) or pass the PWT as an implicit argument — the latter is used for large generic functions to avoid code size explosion. The @_specialize attribute forces specialisation for specific types.

Primary associated types and typed throws (Swift 5.7/6.0)

Swift 5.7 introduced primary associated typesCollection<Int> is now valid syntax, constraining the element type. This enables some Collection<Int> as a function parameter without a full generic signature. Swift 6.0 introduced typed throwsfunc fetch() throws(NetworkError) -> Data declares the exact error type thrown. Callers don't need to catch arbitrary Error but can exhaustively switch on NetworkError. This closes a long-standing expressiveness gap compared to checked exceptions in Java (but without the overhead of checked exceptions).

Specification reference

Apple Inc. The Swift Programming Language. docs.swift.org/swift-book/. Chapters: Protocols, Generics. Swift Evolution: SE-0309 (unlock existentials), SE-0352 (any keyword), SE-0358 (primary associated types), SE-0413 (typed throws). Apple WWDC 2022: "Embrace Swift generics" and "Design protocol interfaces in Swift."

Sources

1
Apple Inc. The Swift Programming Language. docs.swift.org/swift-book/. — Protocols and Generics chapters.
2
Swift Evolution proposals. github.com/apple/swift-evolution. — SE-0352 (any), SE-0358 (primary associated types), SE-0413 (typed throws).
3
Apple WWDC 2022. "Embrace Swift generics." developer.apple.com/videos.
4
Apple WWDC 2022. "Design protocol interfaces in Swift." developer.apple.com/videos.
5
Hudson, P. (2023). Hacking with Swift — Protocols and extensions. hackingwithswift.com.
Source confidence: High Last verified: Primary source: The Swift Programming Language — docs.swift.org/swift-book/