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.
- Equatable / Hashable: synthesized for structs whose
stored properties are all
Equatable/Hashable, and for enums (including those with associated values, when the associated types conform). - Comparable: synthesized for enums without raw
values or with associated values that are themselves
Comparable; ordering follows declaration order.
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.