Skip to content

Protocols

Protocols define a blueprint of methods, properties, and other requirements that types can adopt to provide specific functionality. Any type that meets these requirements conforms to the protocol.

You can extend protocols to add default implementations or additional features that conforming types can use.

Protocol Syntax

Define a protocol like this:

swift
protocol ExampleProtocol {
    // Protocol requirements go here
}

Types adopt protocols by listing them after their name, separated by colons. Multiple protocols are comma-separated:

swift
struct ExampleStruct: FirstProtocol, SecondProtocol {
    // Struct definition
}

For classes with a superclass, list the superclass first:

swift
class ExampleClass: BaseClass, FirstProtocol, SecondProtocol {
    // Class definition
}

Note: Protocol names start with a capital letter, like other Swift types (e.g., Int, String).

Property Requirements

Protocols can require instance or type properties with a specific name and type. They don't specify if it's stored or computed—only the name, type, and whether it's gettable or gettable/settable.

Use { get set } for gettable and settable properties, or { get } for gettable only. These are declared as variables with var.

For type properties, prefix with static in the protocol, even if implemented with class in classes.

Example protocol requiring a property:

swift
protocol NamedEntity {
    var name: String { get }
}

A conforming struct:

swift
struct Employee: NamedEntity {
    var name: String
}
let alice = Employee(name: "Alice Smith")
// alice.name is "Alice Smith"

A more complex class:

swift
class Vehicle: NamedEntity {
    var model: String
    var manufacturer: String?
    init(model: String, manufacturer: String? = nil) {
        self.model = model
        self.manufacturer = manufacturer
    }
    var name: String {
        (manufacturer != nil ? manufacturer! + " " : "") + model
    }
}
let car = Vehicle(model: "Sedan", manufacturer: "AutoCorp")
// car.name is "AutoCorp Sedan"

Method Requirements

Protocols require instance or type methods without bodies. Use static for type methods.

Example:

swift
protocol NumberGenerator {
    func generate() -> Double
}

Implementation with a simple random generator:

swift
class SimpleGenerator: NumberGenerator {
    var seed = 1.0
    let multiplier = 16807.0
    let modulus = 2147483647.0
    func generate() -> Double {
        seed = (seed * multiplier).truncatingRemainder(dividingBy: modulus)
        return seed / modulus
    }
}
let gen = SimpleGenerator()
print("Random: \(gen.generate())")
// Prints something like "Random: 0.000007822"

Mutating Method Requirements

Use mutating for methods that modify the instance in value types (structs/enums).

Example:

swift
protocol Switchable {
    mutating func flip()
}

Conforming enum:

swift
enum LightState: Switchable {
    case off, on
    mutating func flip() {
        self = (self == .off) ? .on : .off
    }
}
var bulb = LightState.off
bulb.flip() // Now .on

Note: Classes don't need mutating for implementations.

Initializer Requirements

Protocols can require initializers without bodies.

swift
protocol Configurable {
    init(config: Int)
}

In classes, mark as required:

swift
class Device: Configurable {
    required init(config: Int) {
        // Setup
    }
}

Subclasses must also implement if overriding.

Failable Initializer Requirements

Supports failable (init?) or implicitly unwrapped failable (init!) initializers, satisfied by matching or nonfailable ones.

Protocols with Semantic Requirements Only

Some protocols define behaviors without methods/properties, like Sendable for concurrency-safe types. Swift standard library examples include Copyable, BitwiseCopyable.

Adopt them normally, even with empty bodies:

swift
struct DataPoint: Copyable {
    var value = 42
}

extension DataPoint: BitwiseCopyable { }

Swift often infers these implicitly.

Protocols as Types

Use protocols in generics, as opaque types, or boxed types for runtime flexibility.

Delegation

Delegation lets a type hand off tasks to another via a protocol.

Example game with delegate for logging:

swift
class CardGame {
    let suits = 4
    weak var delegate: GameLogger?
    
    func play() {
        delegate?.gameStarted(self)
        // Game logic
        delegate?.gameEnded(self)
    }
    
    protocol GameLogger: AnyObject {
        func gameStarted(_ game: CardGame)
        func gameEnded(_ game: CardGame)
    }
}

Logger implementation:

swift
class BasicLogger: CardGame.GameLogger {
    func gameStarted(_ game: CardGame) {
        print("Game began")
    }
    func gameEnded(_ game: CardGame) {
        print("Game over")
    }
}

Usage:

swift
let logger = BasicLogger()
let game = CardGame()
game.delegate = logger
game.play()
// Prints "Game began" and "Game over"

Adding Protocol Conformance with an Extension

Extend existing types to adopt protocols.

Example:

swift
protocol Describable {
    var description: String { get }
}

extension Vehicle: Describable {
    var description: String {
        "A vehicle: \(name)"
    }
}

Conditionally Conforming to a Protocol

Use where for constraints:

swift
extension Array: Describable where Element: Describable {
    var description: String {
        map { $0.description }.joined(separator: ", ")
    }
}

Declaring Protocol Adoption with an Extension

If requirements are met, adopt with empty extension:

swift
struct Animal {
    var species: String
    var description: String { "An animal: \(species)" }
}
extension Animal: Describable {}

Adopting a Protocol Using a Synthesized Implementation

Swift synthesizes Equatable, Hashable, Comparable for simple types.

Example Equatable:

swift
struct Point3D: Equatable {
    var x = 0.0, y = 0.0, z = 0.0
}

Hashable and Comparable similar.

Implicit Conformance to a Protocol

Swift infers Copyable, Sendable, etc. Suppress with ~:

swift
struct Resource: ~Sendable {
    let id: Int
}

Or unavailable extension.

Collections of Protocol Types

Store in arrays:

swift
let items: [Describable] = [car, Animal(species: "Cat")]
for item in items {
    print(item.description)
}

Protocol Inheritance

Inherit protocols:

swift
protocol DetailedDescribable: Describable {
    var detailedDescription: String { get }
}

Class-Only Protocols

Use AnyObject:

swift
protocol ClassSpecific: AnyObject {
    // Definitions
}

Protocol Composition

Combine with &:

swift
protocol Aged {
    var age: Int { get }
}
func greet(person: NamedEntity & Aged) {
    print("Hello \(person.name), age \(person.age)")
}

Include classes.

Checking for Protocol Conformance

Use is, as?, as!:

swift
protocol Measurable {
    var size: Double { get }
}

class Box: Measurable { var size: Double = 10 }
class Region: Measurable { var size: Double = 100 }
class Tool { var weight = 5 }

let things: [AnyObject] = [Box(), Region(), Tool()]

for thing in things {
    if let measurable = thing as? Measurable {
        print("Size: \(measurable.size)")
    } else {
        print("Not measurable")
    }
}

Optional Protocol Requirements

Use @objc and optional:

swift
@objc protocol DataProvider {
    @objc optional func getValue(for key: Int) -> Int
    @objc optional var constantValue: Int { get }
}

Use optional chaining.

Protocol Extensions

Provide defaults:

swift
extension NumberGenerator {
    func generateBool() -> Bool {
        generate() > 0.5
    }
}

Providing Default Implementations

Defaults override optionals in calling.

Adding Constraints to Protocol Extensions

Use where:

swift
extension Collection where Element: Equatable {
    func allSame() -> Bool {
        all { $0 == first }
    }
}

Released under the MIT License.