Protocols

A protocol defines a blueprint of methods, properties, and other requirements that a type can adopt. Any type that satisfies the requirements is said to conform to the protocol. Protocols are the heart of Swift's approach to abstraction — "protocol-oriented programming" — letting unrelated types share a common interface without sharing an inheritance hierarchy.

Protocol syntax and requirements

You define a protocol with the protocol keyword, then list the requirements its conformers must provide. A type adopts a protocol by listing it after a colon, just like a superclass.

Property requirements

A protocol requires a property by name and type, and states whether it must be gettable ({ get }) or both gettable and settable ({ get set }). It never specifies whether the property is stored or computed. Type properties are required with the static keyword.

protocol FullyNamed {
    var fullName: String { get }
}

struct Person: FullyNamed {
    var fullName: String   // satisfied by a stored property
}

class Starship: FullyNamed {
    var prefix: String?
    var name: String
    init(name: String, prefix: String? = nil) {
        self.name = name; self.prefix = prefix
    }
    var fullName: String {        // satisfied by a computed property
        (prefix != nil ? prefix! + " " : "") + name
    }
}

print(Starship(name: "Enterprise", prefix: "USS").fullName)
// → USS Enterprise

Method, mutating, and initializer requirements

Protocols can require instance and type methods (written without a body), mutating methods for value types that need to modify themselves, and even initializers. A class that satisfies an initializer requirement must mark it required so subclasses inherit it.

protocol RandomNumberGenerator {
    func random() -> Double
}

protocol Togglable {
    mutating func toggle()
}

enum Switch: Togglable {
    case off, on
    mutating func toggle() {
        self = (self == .off) ? .on : .off
    }
}

protocol Named {
    init(name: String)
}

class Pet: Named {
    var name: String
    required init(name: String) { self.name = name }
}

Protocols as types

A protocol is a fully fledged type. You can use one as the type of a constant, variable, parameter, return value, or element of a collection. Wherever you use a protocol as a type today, Swift asks you to write any in front of it to make the "existential" (boxed) nature explicit.

struct Dice {
    let sides: Int
    let generator: any RandomNumberGenerator   // protocol as a stored type

    func roll() -> Int {
        Int(generator.random() * Double(sides)) + 1
    }
}

struct LinearCongruential: RandomNumberGenerator {
    var last = 42.0
    mutating func random() -> Double {
        last = (last * 1103515245 + 12345).truncatingRemainder(dividingBy: 2147483648)
        return last / 2147483648
    }
}

Note: the choice between any Protocol (a boxed value of any conforming type) and some Protocol (a fixed but hidden single type) is important. The dedicated lesson on Opaque and Boxed Protocol Types explains exactly when to pick each.

Delegation

Delegation is a design pattern where one type hands off (delegates) responsibilities to an instance of another type. Protocols make it type-safe: you define a delegate protocol, and any conforming type can step in. This pattern is everywhere in Apple's frameworks.

protocol DownloadDelegate: AnyObject {
    func downloadDidFinish(_ url: String)
    func downloadDidFail(_ error: Error)
}

class Downloader {
    weak var delegate: DownloadDelegate?   // weak avoids a reference cycle

    func finish(url: String) {
        delegate?.downloadDidFinish(url)
    }
}

class ViewModel: DownloadDelegate {
    func downloadDidFinish(_ url: String) { print("Got \(url)") }
    func downloadDidFail(_ error: Error) { print("Failed: \(error)") }
}

Declaring the protocol as AnyObject makes it class-only, which is what lets the delegate be marked weak.

Adding conformance with an extension

You can make an existing type conform to a protocol via an extension, keeping the conformance and its requirements grouped in one place.

protocol TextRepresentable {
    var textualDescription: String { get }
}

extension Dice: TextRepresentable {
    var textualDescription: String { "A \(sides)-sided dice" }
}

Conditional conformance

Generic types can conform to a protocol only when their type arguments satisfy some condition, expressed with a where clause on the extension. The standard library uses this heavily — for example, an Array is Equatable only when its elements are.

extension Array: TextRepresentable where Element: TextRepresentable {
    var textualDescription: String {
        let items = self.map { $0.textualDescription }
        return "[" + items.joined(separator: ", ") + "]"
    }
}

let dice = [Dice(sides: 6), Dice(sides: 20)]
print(dice.textualDescription)
// → [A 6-sided dice, A 20-sided dice]

Synthesized conformance

Swift automatically synthesizes the implementation of Equatable, Hashable, and Comparable for many types, so you don't have to write the boilerplate yourself.

struct Vector2D: Equatable, Hashable {
    var x: Double, y: Double
}
// == and hash(into:) are provided for free:
print(Vector2D(x: 1, y: 2) == Vector2D(x: 1, y: 2))   // → true

enum Priority: Comparable {
    case low, medium, high   // low < medium < high, by declaration order
}
print(Priority.low < .high)   // → true

To opt in, just declare conformance (: Equatable) in the type's original declaration or in an extension in the same file.

Protocol inheritance and class-only protocols

A protocol can inherit one or more other protocols, adding further requirements on top. A conforming type must satisfy all inherited requirements. Restrict a protocol to class types by inheriting from AnyObject.

protocol PrettyTextRepresentable: TextRepresentable {
    var prettyDescription: String { get }
}

// class-only protocol — only classes may conform
protocol Resettable: AnyObject {
    func reset()
}

Protocol composition

Sometimes a value must conform to several protocols at once. Combine requirements inline with the & operator instead of defining a new protocol. A composition may also include one class type.

protocol Aged { var age: Int { get } }
protocol Greetable { var name: String { get } }

func wishHappyBirthday(to celebrant: any Greetable & Aged) {
    print("Happy \(celebrant.age)th, \(celebrant.name)!")
}

struct Human: Greetable, Aged {
    var name: String; var age: Int
}
wishHappyBirthday(to: Human(name: "Mei", age: 30))
// → Happy 30th, Mei!

Checking and casting for conformance

Because protocols are types, the type-casting operators work with them: is tests whether a value conforms, and as? / as! downcast to a protocol type.

let objects: [Any] = [Human(name: "Mei", age: 30), 42, "hi"]
for object in objects {
    if let aged = object as? any Aged {
        print("Age is \(aged.age)")
    } else {
        print("Not Aged")
    }
}
// → Age is 30 / Not Aged / Not Aged

Optional protocol requirements

Protocols can declare optional requirements that conforming types need not implement. This is interop-flavored: the protocol must be marked @objc, can only be adopted by classes, and each optional member uses the optional modifier. You call such members with optional chaining.

import Foundation

@objc protocol CounterDataSource {
    @objc optional func increment(forCount count: Int) -> Int
    @objc optional var fixedIncrement: Int { get }
}

class Counter {
    var count = 0
    var dataSource: CounterDataSource?
    func step() {
        if let amount = dataSource?.increment?(forCount: count) {
            count += amount
        } else if let amount = dataSource?.fixedIncrement {
            count += amount
        }
    }
}

Tip: in pure Swift, prefer protocol extensions with default implementations (next section) over @objc optional. They're more idiomatic, work with structs and enums, and don't require the Objective-C runtime.

Protocol extensions and default implementations

You can extend a protocol to provide method, property, and subscript implementations to conforming types. This is the engine of protocol-oriented programming: define behavior once on the protocol and every conformer gets it automatically, while still being free to override.

extension RandomNumberGenerator {
    // default implementation built on the required random()
    func randomBool() -> Bool {
        random() > 0.5
    }
}

// Any conformer gets randomBool() for free:
var gen = LinearCongruential()
print(gen.randomBool())   // → true or false

You can also constrain a protocol extension with a where clause so its members apply only to conformers that also meet extra requirements.

extension Collection where Element: Equatable {
    func allEqual() -> Bool {
        guard let first = self.first else { return true }
        return self.allSatisfy { $0 == first }
    }
}

print([2, 2, 2].allEqual())   // → true
print([1, 2, 3].allEqual())   // → false

Watch out: if a member is provided only in a protocol extension (not declared as a requirement in the protocol itself), calls are dispatched statically based on the compile-time type. A conformer that "overrides" such a member won't be used through a variable typed as the protocol. Declare it as a real requirement when you need dynamic dispatch.

Associated types

An associated type is a placeholder name for a type used in a protocol's requirements; the conforming type supplies the concrete type. Declare them with the associatedtype keyword. This is how the standard library's Sequence and Collection protocols stay generic over their element type.

protocol Container {
    associatedtype Item
    mutating func append(_ item: Item)
    var count: Int { get }
    subscript(i: Int) -> Item { get }
}

struct IntStack: Container {
    // Swift infers Item == Int from the implementations below
    var items: [Int] = []
    mutating func append(_ item: Int) { items.append(item) }
    var count: Int { items.count }
    subscript(i: Int) -> Int { items[i] }
}

Constraining associated types

Associated types can carry constraints, and you can add a where clause to relate them to other associated types. Here we require a container's Item to be Equatable, and define a protocol that pairs a suffix container with the original.

protocol EquatableContainer {
    associatedtype Item: Equatable           // inline constraint
    var count: Int { get }
    subscript(i: Int) -> Item { get }
}

protocol SuffixableContainer: Container {
    // where-clause relating one associated type to another
    associatedtype Suffix: SuffixableContainer where Suffix.Item == Item
    func suffix(_ size: Int) -> Suffix
}

These constraints let the compiler reason precisely about how the pieces fit together — for instance, guaranteeing that a container's suffix holds the same kind of element. Associated types are also the bridge to the Generics chapter, where generic where clauses let you write algorithms across any conforming container.

Wrapping up

Protocols describe what a type can do without dictating how. You've seen property, method, mutating, and initializer requirements; protocols used as types; delegation; conditional and synthesized conformance; inheritance, class-only protocols, and composition; conformance checks; optional requirements; protocol extensions with default implementations; and associated types. Together they make Swift's protocol-oriented style both flexible and safe — and they set the stage for generics.