Swift is often called protocol-oriented: Equatable, Codable, Sendable and SwiftUI's View are all protocols.
A protocol describes requirements — properties, methods, initializers — without implementing them. Any struct, class or enum can conform by providing them:
protocol Shape {
var area: Double { get }
func describe() -> String
}Property requirements say only { get } or { get set }; a stored or computed property can satisfy them. Method requirements that modify a value type are marked mutating.
Protocols are types: let shapes: [any Shape] holds a mix of conforming types (an existential; the any keyword makes the boxing explicit).
Protocol extensions add default implementations and helper methods to every conforming type. This is how the standard library gives every Collection methods like map, contains and isEmpty from just a few requirements.
Protocols can inherit from others (protocol Named: CustomStringConvertible), be composed (Shape & Named), and be restricted to classes with AnyObject (needed for weak delegate references).
Many standard protocols get compiler-synthesized conformances: Equatable, Hashable, Comparable (for enums), Codable.
A subtle point: a method declared only in a protocol extension (not as a requirement) is statically dispatched. If a conforming type defines the same method, calls through any Protocol still run the extension version. Declare it as a requirement to get dynamic dispatch.
Example
protocol Shape {
var area: Double { get }
var name: String { get }
}
extension Shape {
var name: String { "shape" } // default
func describe() -> String { "\(name) with area \(area)" }
}
struct Square: Shape {
var side: Double
var area: Double { side * side }
var name: String { "square" }
}
struct Circle: Shape {
var r: Double
var area: Double { 3.14 * r * r } // uses default name
}
let shapes: [any Shape] = [Square(side: 2), Circle(r: 1)]
for s in shapes { print(s.describe()) }
// square with area 4.0
// shape with area 3.14
let largest = shapes.map(\.area).max() ?? 0
print(largest) // 4.0Remember
- Protocols declare requirements; types of any kind can conform
- Protocol extensions provide default implementations
- Only requirements are dynamically dispatched through
any P
Watch out for
- Expecting an override of an extension-only method to be called through the protocol type.