Protocols
19.1 Defining and Adopting Protocols
protocol Shape {
var name: String { get } // read-only property requirement
var area: Double { get }
func describe() -> String
static var sides: Int { get }
init(size: Double) // initializer requirement
}
struct Square: Shape {
let size: Double
var name: String { "Square" }
var area: Double { size * size }
static var sides: Int { 4 }
init(size: Double) { self.size = size }
func describe() -> String { "\(name) with area \(area)" }
}
final class Circle: Shape {
let radius: Double
var name: String { "Circle" }
var area: Double { .pi * radius * radius }
static var sides: Int { 0 }
required init(size: Double) { radius = size } // `required` in non-final classes
func describe() -> String { "\(name) r=\(radius)" }
}
let shapes: [any Shape] = [Square(size: 2), Circle(size: 1)]
for s in shapes { print(s.describe()) }19.2 Protocol Extensions and Default Implementations
Methods declared in the protocol are dynamically dispatched (customization points). Methods only in extensions are statically dispatched. A conformer's version won't be called through a protocol-typed value.
protocol Greetable {
var name: String { get }
func greet() -> String
}
extension Greetable {
func greet() -> String { "Hello, \(name)!" } // default implementation
func shout() -> String { greet().uppercased() } // extra method for all conformers
}
struct Person: Greetable { let name: String }
Person(name: "Ana").shout()19.3 Mutating Requirements, Class-Only Protocols and Composition
protocol Togglable { mutating func toggle() }
enum Switch: Togglable {
case on, off
mutating func toggle() { self = self == .on ? .off : .on }
}
protocol Delegate: AnyObject { func didFinish() } // class-only (allows weak references)
class Worker { weak var delegate: Delegate? }
protocol Named { var name: String { get } }
protocol Aged { var age: Int { get } }
func wish(_ celebrator: some Named & Aged) { // protocol composition
print("Happy \(celebrator.age), \(celebrator.name)!")
}19.4 Associated Types
protocol Container {
associatedtype Item
mutating func append(_ item: Item)
var count: Int { get }
subscript(i: Int) -> Item { get }
}
struct Stack<Element>: Container {
private var items: [Element] = []
mutating func push(_ item: Element) { items.append(item) }
mutating func pop() -> Element? { items.popLast() }
mutating func append(_ item: Element) { push(item) } // Item inferred as Element
var count: Int { items.count }
subscript(i: Int) -> Element { items[i] }
}
protocol Repository<Model> { // primary associated type (Swift 5.7)
associatedtype Model
func fetchAll() async throws -> [Model]
}
func load(from repo: some Repository<String>) async throws { _ = try await repo.fetchAll() }19.5 Conditional Conformance and Self Requirements
extension Stack: Equatable where Element: Equatable {} // conformance only when Element is Equatable
protocol Copyable { func copy() -> Self }
extension Array where Element: Numeric {
var total: Element { reduce(0, +) }
}
[1, 2, 3].total19.6 Protocol-Oriented Programming (POP)
Start with protocols, add behavior via extensions, prefer value types, and compose small protocols instead of deep class hierarchies.
Uses: dependency injection (protocol NetworkClient), testability (mock conformers), shared behavior across structs/enums/classes.