How it works
The explanation, with examples you can run.
Requirements and defaults
protocol Shape { var area: Double { get } // requirement func describe() -> String // requirement, with a default below} extension Shape { func describe() -> String { "a shape of area \(area)" } // default implementation func isLarger(than other: some Shape) -> Bool { area > other.area } // extra method, not a requirement} struct Square: Shape { var side: Double var area: Double { side * side } func describe() -> String { "a square with side \(side)" } // overrides the default} struct Circle: Shape { var radius: Double var area: Double { 3.14159 * radius * radius } // uses the default describe()} let shapes: [any Shape] = [Square(side: 2), Circle(radius: 1)]for s in shapes { print(s.describe()) }print(Square(side: 2).isLarger(than: Circle(radius: 1))) // trueConformance can also be added in an extension, even to types you don't own: extension Int: Shape { ... }. Grouping each conformance in its own extension is common Swift style.
The dispatch trap
Calls to a requirement go through the type's witness table, so the conforming type's own implementation always runs. A method that exists only in the protocol extension is not in that table: it is chosen by the static type at the call site.
protocol Greeter {}extension Greeter { func hi() -> String { "generic hi" } } // not a requirement struct English: Greeter { func hi() -> String { "hello" } } let direct = English()let boxed: any Greeter = English()print(direct.hi()) // helloprint(boxed.hi()) // generic hi: resolved statically through the protocolThe fix is to declare func hi() -> String inside protocol Greeter. The same thing happens in generic code (func greet<T: Greeter>(_ g: T) calls the extension version) and is the Swift twin of Kotlin's statically resolved extension functions.
Constrained extensions
where clauses add members only for some conforming types:
extension Collection where Element: Numeric { func total() -> Element { reduce(0, +) }}print([1, 2, 3].total()) // 6print([0.5, 0.25].total()) // 0.75This is how the standard library gives Array a sorted() only when its elements are Comparable, and == only when they are Equatable.
Synthesised conformances
For Equatable, Hashable, Comparable (enums) and Codable, the compiler writes the implementation when all stored properties (or associated values) conform. You only declare the conformance. Two protocols that matter beyond this: Sendable (safe to share across concurrency domains, covered later) and Identifiable, which SwiftUI uses to track items in lists.
Protocols with associated types and the some and any keywords are the next topic.
Check yourself
3 questions. Get them all right to finish this topic.
0 of 3 right so far. Get all three to finish this topic.