Swift Protocols

Protocols define a blueprint of methods and properties that any class, struct, or enum can adopt, which is the basis of Swift's protocol-oriented programming style.

Defining and Conforming to Protocols

A protocol declares requirements — properties, methods, or initializers — without providing an implementation. Any type that conforms adds its own implementation of every requirement. Structs, classes, and enums can all conform to the same protocol, which lets very different kinds of types be used interchangeably wherever the protocol is expected.

Protocols as Types

Once a protocol exists, it can be used as a type in its own right: as a function parameter, a return type, or the element type of a collection. Code written against the protocol works with any current or future conforming type, which is the essence of protocol-oriented programming.

  • Property requirements specify a name, type, and whether they need { get } or { get set }.
  • Methods on a protocol that mutate a value-type instance must be marked mutating in the protocol declaration.
  • A protocol can require specific initializers, which conforming classes must mark required.
  • Protocols can inherit from other protocols, combining and extending their requirements.
  • Protocol composition with & (for example, Codable & Equatable) creates an inline type requiring conformance to several protocols at once.

A struct and a class conforming to the same protocol

protocol Greetable {
    var name: String { get }
    func greet() -> String
}

struct Visitor: Greetable {
    var name: String
    func greet() -> String { "Hello, I'm \(name), a visitor." }
}

class Employee: Greetable {
    var name: String
    init(name: String) { self.name = name }
    func greet() -> String { "Hi, I'm \(name), staff." }
}

let people: [Greetable] = [Visitor(name: "Lee"), Employee(name: "Kim")]
for person in people {
    print(person.greet())
}

Protocol composition as a parameter type

protocol Identifiable {
    var id: String { get }
}

protocol Named {
    var name: String { get }
}

struct Product: Identifiable, Named {
    var id: String
    var name: String
}

func describe(_ item: Identifiable & Named) -> String {
    "\(item.id): \(item.name)"
}

let product = Product(id: "P100", name: "Keyboard")
print(describe(product))   // P100: Keyboard
Note: Use protocol composition (A & B) when a function genuinely needs both sets of requirements, instead of defining a brand-new protocol that just inherits from both — composition keeps small, focused protocols reusable.

Protocol Extensions and Default Implementations

An extension on a protocol can supply a default implementation for one of its requirements, or add entirely new methods available to every conforming type. A conforming type automatically gets the default unless it provides its own implementation, which overrides the default for that specific type.

Default implementations via a protocol extension

protocol Loggable {
    var logPrefix: String { get }
    func log(_ message: String)
}

extension Loggable {
    var logPrefix: String { "[LOG]" }
    func log(_ message: String) {
        print("\(logPrefix) \(message)")
    }
}

struct NetworkClient: Loggable {}

struct Database: Loggable {
    var logPrefix: String { "[DB]" }
}

NetworkClient().log("Request sent")   // [LOG] Request sent
Database().log("Query executed")     // [DB] Query executed
ConceptDescription
Protocol requirementA method or property every conforming type must implement
Default implementationBehavior provided in a protocol extension; used automatically when a type doesn't supply its own
Protocol composition (A & B)A single type constraint requiring conformance to multiple protocols at once
mutating keywordRequired on protocol methods that modify a value type's own properties
Note: A protocol extension method that isn't declared in the protocol itself is dispatched statically. If you call it through a variable typed as the protocol, Swift uses the extension's default even if the concrete type defines a method with the same name that isn't part of the protocol's requirements — a common source of surprising bugs.

Exercise: Swift Protocols

What does a Swift protocol define?