Protocols

This section documents the Swift 6 language mode as shipped by Swift 6.3, as published in The Swift Programming Language at docs.swift.org, which is the reference these pages are written and verified against. 6.4-beta-only features are always flagged as such — never presented as baseline.

This content was generated with the assistance of AI and should be verified against docs.swift.org before being relied on in production.

This section’s bibliography lists the reference material consulted while preparing these pages.

A protocol defines a blueprint of properties, methods, subscripts and other requirements that suit a particular task or piece of functionality — adopted by a class, structure, or enumeration to provide an actual implementation of those requirements. Protocols are what makes Swift’s protocol-oriented style of design possible: composing behavior by adopting protocols instead of, or in addition to, subclassing (see Inheritance).

Protocol Syntax and Requirements

protocol FullyNamed {
    var fullName: String { get }                 // property requirement: readable, `get`-only here
}

protocol Container {
    associatedtype Item
    var count: Int { get }
    mutating func append(_ item: Item)            // `mutating` is required on any method that may need it,
    subscript(index: Int) -> Item { get }          // even though a class conformer will ignore the keyword
    init(item: Item)                                // initializer requirement
}

struct Person: FullyNamed {
    var fullName: String
}

A protocol declares requirements but provides no implementation of its own: a property requirement names a type and whether it must be gettable ({ get }) or gettable-and-settable ({ get set }), never var/let; a method requirement omits a body; a mutating method requirement lets a value-type conformer mark its implementation mutating (a class conformer simply omits mutating, since reference types don’t need it); a subscript requirement is written like a computed property’s; and an initializer requirement forces every non-final conforming class to mark its matching initializer required (see Initialization and Deinitialization), so that it — and every subclass — is guaranteed to provide it.

Protocols with Only Semantic Requirements

protocol Togglable {
    mutating func toggle()          // the *signature* is the only formal requirement...
}
// ...but Togglable's documentation states the *semantic* requirement that toggle() must alternate between
// exactly two states -- nothing in the type system enforces that meaning, only convention and documentation do.

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

Not every protocol requirement can be captured in Swift’s type system: Togglable above formally requires only a method with a matching signature, but its real contract — "toggling flips between exactly two states" — is a semantic requirement, documented in prose and trusted rather than compiler-checked. Many standard-library protocols (Equatable, Hashable, Comparable) work the same way: they name axioms (Equatable’s `== must be reflexive, symmetric and transitive) that a conformer is expected to honor even though nothing stops a broken implementation from compiling.

Protocols as Types, any, and Delegation

protocol RandomNumberGenerator {
    func random() -> Double
}

struct LinearCongruentialGenerator: RandomNumberGenerator {
    func random() -> Double { 0.42 }         // a fixed stand-in for a real generator
}

func rollDice(using generator: some RandomNumberGenerator) -> Int {   // a concrete, opaque conforming type
    Int(generator.random() * 6) + 1
}

var generators: [any RandomNumberGenerator] = [LinearCongruentialGenerator()]   // `any`: a boxed existential

protocol DiceGameDelegate: AnyObject {                    // a delegate protocol, typically class-only (see below)
    func gameDidStart()
    func gameDidEnd()
}

final class DiceGame {
    weak var delegate: DiceGameDelegate?     // `weak` avoids a reference cycle -- see Automatic Reference Counting

    func play() {
        delegate?.gameDidStart()
        // ... play the game ...
        delegate?.gameDidEnd()
    }
}

A protocol name can be used as a type in its own right, wherever a type is expected. Written directly (some RandomNumberGenerator on a parameter, or as a generic constraint) it names a specific, statically-known conforming type the compiler still tracks; written as any RandomNumberGenerator, it erases that specific type into a boxed existential value that can hold any conformer, at the cost of dynamic dispatch and, for a non-class-bound protocol, potential heap allocation — any is required whenever a protocol type is stored, returned, or otherwise needs to abstract over which concrete type is actually inside (see Opaque and Boxed Protocol Types for the full some vs. any story). Delegation is a design pattern built directly on this: a type (DiceGame) hands off part of its responsibility to an external object conforming to a delegate protocol, held as a weak reference to avoid the strong reference cycle a delegate relationship would otherwise create.

Conformance via Extensions and Conditional Conformance

protocol TextRepresentable {
    var textualDescription: String { get }
}

struct Hamster {
    var name: String
}

extension Hamster: TextRepresentable {                    // conformance declared separately from the type itself
    var textualDescription: String { "A hamster named \(name)" }
}

struct Stack<Element> {
    var items: [Element] = []
    mutating func push(_ item: Element) { items.append(item) }
    mutating func pop() -> Element { items.removeLast() }
}

extension Stack: TextRepresentable where Element: TextRepresentable {   // *conditional* conformance
    var textualDescription: String {
        "[" + items.map(\.textualDescription).joined(separator: ", ") + "]"
    }
}

A type conforms to a protocol either at its own declaration or later via an extension — either way, once the conformance exists, instances of the type can be used anywhere that protocol is required. A generic type can conform to a protocol conditionally, only when its type parameter itself satisfies some requirement (where Element: TextRepresentable above): Stack<Hamster> conforms to TextRepresentable, but Stack<Int> does not, since Int doesn’t.

Synthesized Equatable/Hashable/Comparable/Codable

struct Position: Equatable, Hashable, Comparable {     // every requirement synthesized -- no code needed below
    let file: String
    let line: Int

    static func < (lhs: Position, rhs: Position) -> Bool {   // Comparable still needs `<` written by hand
        (lhs.file, lhs.line) < (rhs.file, rhs.line)
    }
}

struct Coordinate: Codable {                            // Encodable + Decodable, fully synthesized
    let latitude: Double
    let longitude: Double
}

The compiler synthesizes == and hash(into:) for free for a struct or enum whose stored properties (or associated values) are all themselves Equatable/Hashable, so long as the conformance is declared with no custom implementation of the requirement it synthesizes; Comparable synthesis only covers ordering < for a subset of cases in specific situations (a raw-value or no-payload enum), so Position above still writes < itself. Codable (Encodable & Decodable) is synthesized whenever every stored property is itself Codable, producing member-by-member encoding/decoding with no code written at all — see Codable and Serialization for customizing it.

Implicit Conformance and Suppressing It (~Copyable, ~Escapable)

struct Ordinary {}                          // implicitly Copyable and Escapable -- true of almost every type

struct FileHandle: ~Copyable {              // suppresses the implicit Copyable conformance (see Structures and Classes)
    let descriptor: Int32
}

struct BufferView<T>: ~Escapable {          // suppresses Escapable: a value that must not outlive what it borrows from
    let pointer: UnsafePointer<T>
}

Every Swift type is implicitly Copyable (it may be copied freely) and Escapable (an instance may outlive the scope it was created in) unless it opts out. ~Copyable and ~Escapable suppress those two implicit conformances specifically — they are not ordinary protocols a type opts into, but markers removing a default every type otherwise gets, used for types whose whole point is a single, non-duplicable owner (a file handle, a lock) or a value tied to some other value’s lifetime (a buffer view over borrowed memory). See Structures and Classes for ~Copyable in full and Memory Safety and Unsafe Pointers for `~Escapable’s lifetime-dependency use cases.

Collections of Protocol Types

let things: [TextRepresentable] = [Hamster(name: "Fluffy"), Stack<Hamster>()]  // a homogeneous array of a protocol type
for thing in things {
    print(thing.textualDescription)          // dispatches to each element's own conformance, not a common base class
}

An array (or any collection) typed as [SomeProtocol] can hold instances of any mix of conforming types — structs, classes, and enums together — with no shared base class required at all, which is exactly the composition-friendly alternative to a class hierarchy discussed in Inheritance.

Protocol Inheritance, Class-Only Protocols, and Composition

protocol Named {
    var name: String { get }
}

protocol Aged {
    var age: Int { get }
}

protocol PrestigiousSociety: Named, Aged {}   // protocol inheritance: requires everything Named and Aged require

protocol Cloneable: AnyObject {                // class-only: only a class may conform
    func clone() -> Self
}

func describe(_ value: Named & Aged) -> String {    // protocol composition: BOTH sets of requirements, inline
    "\(value.name) is \(value.age)"
}

A protocol can inherit from one or more other protocols with the same : syntax a class uses, adding every inherited requirement to its own; a protocol restricted with : AnyObject may only be adopted by a class, useful whenever conforming code needs reference semantics (an identity check, a weak reference) rather than a value type. SomeProtocol & OtherProtocol — protocol composition — names, inline, any type conforming to both at once without declaring a new named protocol for the combination, as `describe’s parameter type does above.

Checking Conformance with is / as?

let candidate: Any = Hamster(name: "Fluffy")
if candidate is TextRepresentable {                       // `is`: does this value conform?
    print("conforms")
}
if let describable = candidate as? TextRepresentable {     // `as?`: conditionally cast to the protocol type
    print(describable.textualDescription)
}

is asks a yes/no question about whether a value’s dynamic type conforms to a given protocol (or is a given class, or subclass); as? performs the same check and, on success, produces the value typed as the protocol (or class) itself, nil otherwise — the same two operators used for ordinary class-hierarchy casts, covered in full in Type Casting and Reflection.

@objc optional Requirements

import Foundation

@objc protocol CounterDataSource {
    @objc optional func increment(forCount count: Int) -> Int    // may be left unimplemented by a conformer
    @objc optional var fixedIncrement: Int { get }
}

class ThreeSource: NSObject, CounterDataSource {
    let fixedIncrement = 3          // implements only one of the two optional requirements
}

optional requirements exist only inside an @objc protocol (which in turn means only a class — ultimately one rooted in NSObject — can conform), and let a conformer skip some requirements entirely; calling an optional requirement always goes through optional chaining (someDataSource.increment?(forCount: 4)) or an explicit responds(to:) check, since the compiler cannot guarantee it was implemented. This exists specifically for interoperating with Objective-C APIs that predate Swift’s own preferred alternative — a protocol extension supplying a default implementation, covered next — and is unavailable to a pure-Swift protocol.

Protocol Extensions with Default Implementations and Constraints

classDiagram class TextRepresentable { <> +textualDescription: String } class PrettyTextRepresentable { <> +prettyTextualDescription: String } TextRepresentable <|-- PrettyTextRepresentable : inherits PrettyTextRepresentable ..> TextRepresentable : default implementation\nin a protocol extension class Hamster class Stack~Element~ TextRepresentable <|.. Hamster : conforms TextRepresentable <|.. Stack~Element~ : conforms (conditional)
protocol PrettyTextRepresentable: TextRepresentable {
    var prettyTextualDescription: String { get }
}

extension PrettyTextRepresentable {
    var prettyTextualDescription: String {                       // a default implementation every conformer gets
        "✨ " + textualDescription + " ✨"                          // free, in terms of the *other* requirement
    }
}

extension Collection where Element: Equatable {                  // an extension constrained to a subset of conformers
    func allEqual() -> Bool {
        guard let first else { return true }
        return allSatisfy { $0 == first }
    }
}

A protocol extension can supply a default implementation for one of its own requirements (or for a member the protocol doesn’t even require), which every conforming type gets automatically unless it provides its own, more specific implementation instead — Swift always calls the more specific one, whichever is available. A where clause on the extension (as on Collection above) further restricts the default to only those conformers satisfying an extra constraint, letting a single protocol’s extensions layer general-purpose behavior and behavior specific to certain kinds of conformers side by side.

Protocol-Oriented Design: the Standard Library as a Case Study

The standard library’s own collection types are the canonical example of protocol-oriented design: Sequence, Collection, BidirectionalCollection and RandomAccessCollection form a protocol hierarchy where each level adds requirements (and, via extensions, dozens of default-implemented methods like map, filter, and allEqual above) that every conformer — Array, Set, Dictionary, a custom type entirely — gets without writing them itself, exactly the mechanism shown for PrettyTextRepresentable above (see Collections for the hierarchy itself). This is the practical payoff of preferring protocols over a class hierarchy for shared behavior: a struct (which cannot subclass anything) can still adopt Collection and immediately gain the same rich, tested behavior a class-based design would have needed a common superclass to share — with no forced "is a" relationship, no single-inheritance ceiling, and conformance completely decoupled from a type’s storage strategy.

See Also