Swift Protocols

Describe abilities with protocols, make structs, classes and enums conform, share default behaviour with protocol extensions, and use standard protocols like Equatable, Comparable and Identifiable.

Lesson 15 of 43Intermediate18 min read

A protocol describes what a type can do, without saying how. It's a list of requirements, such as properties and methods, that any conforming type promises to provide. A Payable protocol might require a pay(amount:) method; a card, a voucher and a gift card can all conform, each paying in its own way.

Protocols are at the heart of Swift. Apple describes Swift as a protocol-oriented language: instead of building deep class hierarchies, you describe abilities with protocols and give them default behaviour with protocol extensions. Structs, enums and classes can all conform, and a type can conform to as many protocols as it needs.

You've already used protocols without noticing: comparing values with == relies on Equatable, sorting relies on Comparable, and SwiftUI's View is a protocol. In this lesson you'll write your own protocols, conform to them, add default implementations, and adopt the standard protocols every Swift developer uses.

Declaring and adopting a protocol

Swift
protocol Describable {
    var title: String { get }
    func describe() -> String
}

struct Book: Describable {
    let title: String
    let author: String
    func describe() -> String { "\(title) by \(author)" }
}
  • Property requirements state the type and whether they're read-only ({ get }) or also writable ({ get set }). A let or computed property satisfies { get }.
  • Conform by adding the protocol after a colon: struct Book: Describable.
  • A type can conform to several protocols, separated by commas, and a class lists its superclass first.
  • If a requirement is missing, the compiler shows an error and offers to add stubs.

Protocols as types

A protocol can be used as a type, so one variable or array can hold different conforming types:

Swift
let items: [any Describable] = [Book(...), Film(...), Podcast(...)]
for item in items { print(item.describe()) }

any Describable means "a value of some type that conforms". Functions can accept protocols too, which lets them work with any conforming type. some Describable instead means "one specific conforming type, chosen by the code", which is how SwiftUI's var body: some View works.

Protocol extensions

A protocol extension adds methods and computed properties to every conforming type, including default implementations of requirements:

Swift
extension Describable {
    func printDescription() { print(describe()) }
    func describe() -> String { "Item: \(title)" }     // default
}

Types can use the default or provide their own. This is how Swift shares behaviour without inheritance: a struct can't inherit from another struct, but it can conform to protocols that bring ready-made methods with them.

Standard protocols you'll use constantly

ProtocolGives youUsually automatic for
Equatable== and !=Structs and enums whose properties are Equatable
HashableUse in sets and as dictionary keysSame as above
Comparable<, sorting, min, maxYou implement <
IdentifiableAn id property, used by SwiftUI listsYou add let id
CustomStringConvertibleA custom description for printingYou implement description
CodableJSON encoding and decodingStructs whose properties are Codable

Just declare conformance, such as struct Point: Equatable, Hashable, and Swift writes the implementation when it can.

Protocol inheritance and composition

A protocol can build on another one. A type that adopts the child protocol must meet both sets of requirements:

Swift
protocol Named {
    var name: String { get }
}
protocol Employee: Named {
    var role: String { get }
}
struct Developer: Employee {
    let name: String
    let role: String
}

When a function needs several abilities at once, combine protocols with & instead of inventing a new protocol just for that function:

Swift
protocol Aged { var age: Int { get } }

func greet(_ person: some Named & Aged) {
    print("\(person.name), \(person.age)")
}

Any type that conforms to both Named and Aged can be passed in.

Two other modifiers appear often. A method that changes a struct or enum must be declared mutating in the protocol too, as in mutating func toggle(); classes conforming to it simply leave the word out. And protocol Delegate: AnyObject limits a protocol to classes, which lets you hold conforming objects in a weak property.

Swift Protocols Syntax

Protocol syntax

protocol Named {
    var name: String { get }
    func greet() -> String
}
extension Named {
    func greet() -> String { "Hi, \(name)" }
}
struct User: Named, Equatable { let name: String }
let things: [any Named] = [User(name: "Ana")]
func show(_ x: some Named) { print(x.greet()) }
struct Score: Comparable {
    let value: Int
    static func < (a: Score, b: Score) -> Bool { a.value < b.value }
}

Swift Protocols Summary

  • A protocol lists property and method requirements; types conform by providing them.
  • Structs, classes and enums can conform to many protocols.
  • any Protocol stores any conforming value; some Protocol means one specific hidden type.
  • Protocol extensions add default implementations and shared behaviour.
  • Equatable, Hashable, Comparable, Identifiable, CustomStringConvertible and Codable are everyday protocols.

Swift Protocols Examples

Each example shows the code, the output it produces and what each part does.

Example 1

Payments through one protocol

Swift
protocol PaymentMethod {
    var label: String { get }
    func charge(_ amount: Double) -> Bool
}

extension PaymentMethod {
    func receipt(_ amount: Double) -> String { "\(label) payment of £\(amount)" }
}

struct Card: PaymentMethod {
    let label = "Card"
    func charge(_ amount: Double) -> Bool { amount <= 500 }
}

struct Voucher: PaymentMethod {
    let label = "Voucher"
    let balance: Double
    func charge(_ amount: Double) -> Bool { amount <= balance }
}

func checkout(with method: any PaymentMethod, amount: Double) {
    let result = method.charge(amount) ? "approved" : "declined"
    print("\(method.receipt(amount)): \(result)")
}

checkout(with: Card(), amount: 89.99)
checkout(with: Voucher(balance: 25), amount: 40)
Output
Card payment of £89.99: approved
Voucher payment of £40.0: declined
Line-by-line explanation
  1. Lines 1 to 4The protocol lists what every payment method must provide.
  2. Lines 6 to 8An extension gives every conforming type a receipt method, without each type writing its own.
  3. Lines 10 to 13A struct conforms by providing label and charge.
  4. Line 21checkout accepts any conforming type, so new payment methods need no changes here.
Example 2

Equatable, Comparable and CustomStringConvertible

Swift
struct Version: Comparable, CustomStringConvertible {
    let major: Int
    let minor: Int

    static func < (a: Version, b: Version) -> Bool {
        (a.major, a.minor) < (b.major, b.minor)
    }

    var description: String { "v\(major).\(minor)" }
}

let releases = [Version(major: 1, minor: 10), Version(major: 1, minor: 2), Version(major: 2, minor: 0)]
print(releases.sorted())
print(releases.max()!)
print(Version(major: 1, minor: 2) == Version(major: 1, minor: 2))
Output
[v1.2, v1.10, v2.0]
v2.0
true
Line-by-line explanation
  1. Line 1Comparable includes Equatable, and Swift writes == automatically because the properties are Equatable.
  2. Lines 5 to 7Implementing < is all sorting needs; tuples compare item by item, so 1.10 comes after 1.2.
  3. Line 9description controls how the value prints.
  4. Lines 13 to 14sorted() and max() work because Version is Comparable.
Example 3

Identifiable and protocol-typed arrays

Swift
import Foundation

protocol Shape {
    var name: String { get }
    func area() -> Double
}

struct Circle: Shape, Identifiable {
    let id = 1
    let radius: Double
    let name = "Circle"
    func area() -> Double { 3.14159 * radius * radius }
}

struct Square: Shape {
    let side: Double
    let name = "Square"
    func area() -> Double { side * side }
}

let shapes: [any Shape] = [Circle(radius: 1), Square(side: 3)]
for shape in shapes {
    print("\(shape.name): \(String(format: "%.2f", shape.area()))")
}
let total = shapes.reduce(0) { $0 + $1.area() }
print("Total: \(String(format: "%.1f", total))")
Output
Circle: 3.14
Square: 9.00
Total: 12.1
Line-by-line explanation
  1. Lines 8 to 9Identifiable requires an id; SwiftUI uses it to tell list rows apart.
  2. Line 21One array holds different types, because both conform to Shape.
  3. Line 25reduce works through the protocol, calling each shape's own area. String(format:) comes from Foundation, imported on line 1.

Common Swift Protocols Mistakes

Missing a requirement

Don't
struct Podcast: Describable {
    let title: String              // error: type 'Podcast' does not conform to protocol 'Describable'
}
Do
struct Podcast: Describable {
    let title: String
    func describe() -> String { "Podcast: \(title)" }
}

Writing == by hand when Swift can do it

Don't
struct Point { let x: Int; let y: Int }
// hand-written == that's easy to get wrong when properties are added
Do
struct Point: Equatable { let x: Int; let y: Int }

A huge protocol

Don't
protocol Everything { func save(); func load(); func render(); func validate(); func share() }
Do
protocol Savable { func save() }
protocol Shareable { func share() }

Small, focused protocols are easier to adopt and test.

Swift Protocols in Real Projects

Protocols in Apple's frameworks

SwiftUI is built on protocols: every screen is a struct conforming to View. Lists need Identifiable data, JSON uses Codable, and Hashable lets values go into sets and navigation paths. App architectures depend on protocols such as UserRepository so a real network implementation can be swapped for a fake one in previews and tests.

Swift Protocols FAQ

What is the difference between a protocol and a class in Swift?

A class is a concrete type with stored data and code, and a type can inherit from only one class. A protocol describes requirements without storing data, and any struct, enum or class can adopt as many protocols as it likes. Protocols describe what a type can do; classes describe what it is.

What is the difference between some and any in Swift?

some Protocol means one specific concrete type that the compiler knows, even if you don't name it, so some View always returns the same view type. any Protocol is a box that can hold any conforming type and change between them, which you need for mixed arrays such as [any Shape].

Can a Swift protocol have default implementations?

Not inside the protocol itself, but a protocol extension can provide them: extension Describable { func describe() -> String { "Item: \(title)" } }. Conforming types then get that implementation unless they write their own.

Can structs conform to protocols in Swift?

Yes. Structs, enums and classes can all adopt protocols. This is a big part of why Swift prefers structs: they can't inherit, but protocols and protocol extensions let them share behaviour anyway.

Why does my type not conform to a Swift protocol?

It's missing a requirement, or one doesn't match exactly, such as a property being let when the protocol needs { get set }, or a method's labels or types differing. Xcode's error lists what's missing and offers a fix that adds stubs for the missing requirements.

Swift Protocols Exercises

Answer each one and check it instantly. If you get stuck, open the hint before the answer.

Fill in the blankseasy

Complete the declaration so Book conforms to Describable.

struct Book Describable {
    let title: String
    func describe() -> String { title }
}
Multiple choiceeasy

Which types can conform to protocols?

Predict the outputmedium

What does this print?

protocol Greeter { func greet() -> String }
extension Greeter { func greet() -> String { "Hello" } }
struct English: Greeter {}
struct French: Greeter { func greet() -> String { "Bonjour" } }
print(English().greet(), French().greet())
Multiple choicemedium

What does adding Equatable to a struct with Equatable properties give you?

Multiple choicehard

What's the difference between some View and any View?

Swift Protocols Quiz

5 questions to check what you learned.

  1. What does a protocol describe?

    • How data is stored
    • A class hierarchy
    • Requirements (properties and methods) that conforming types must provide
    • A file format
  2. What is a protocol extension used for?

    • Adding default implementations and shared methods to every conforming type
    • Creating new protocols
    • Inheriting from classes
    • Removing requirements
  3. Which protocol do SwiftUI lists need for their data?

    • Codable
    • Identifiable
    • Comparable
    • Equatable
  4. What must you implement to make a type Comparable?

    • ==
    • description
    • hash(into:)
    • The < operator
  5. How many protocols can a struct conform to?

    • One
    • Two
    • As many as it needs
    • None