Protocols

Reviewed & published by Brayan K

Protocols are Swift's way of describing what a type can do without saying how. You'll define and adopt protocols, list property and method requirements, use a protocol as a type, and add shared default behaviour with protocol extensions.

Part of the free Swift course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.

What You'll Learn in This Lesson

1️⃣ Defining & Adopting a Protocol

A protocol lists requirements — properties and methods — with no bodies. A type adopts it by listing it after a colon and then conforming: providing an implementation for every requirement.

// A protocol is a blueprint: it lists requirements but no implementation.
protocol Animal {
    var name: String { get }     // a read-only property requirement
    func makeSound() -> String   // a method requirement (no body here)
}

// A type ADOPTS the protocol by listing it after a colon, then provides
// implementations for every requirement.
struct Dog: Animal {
    let name: String
    func makeSound() -> String { return "Woof" }
}

struct Cat: Animal {
    let name: String
    func makeSound() -> String { return "Meow" }
}

let rex = Dog(name: "Rex")
print("\(rex.name) says \(rex.makeSound())")   // Rex says Woof

2️⃣ Protocol as a Type

Once several types conform to the same protocol, you can treat them uniformly by using the protocol as a type — for example an array of Animal holding both dogs and cats.

// Because Dog and Cat both conform to Animal, you can treat them uniformly
// by using the PROTOCOL as a type.
protocol Animal {
    var name: String { get }
    func makeSound() -> String
}
struct Dog: Animal { let name: String; func makeSound() -> String { "Woof" } }
struct Cat: Animal { let name: String; func makeSound() -> String { "Meow" } }

// An array of 'any Animal' can hold both Dogs and Cats:
let pets: [Animal] = [Dog(name: "Rex"), Cat(name: "Mia")]

for pet in pets {
    print("\(pet.name): \(pet.makeSound())")
}

3️⃣ Default Implementations

A protocol extension can supply default behaviour. Conformers inherit it automatically and may override it. This is how Swift shares code without class inheritance.

// A protocol extension can supply a DEFAULT implementation. Conformers get
// it for free unless they provide their own.
protocol Greeter {
    var name: String { get }
    func greet() -> String
}

extension Greeter {
    // Default behaviour shared by every conformer:
    func greet() -> String {
        return "Hello, I'm \(name)"
    }
}

struct Person: Greeter {
    let name: String
    // No greet() here — it uses the default from the extension.
}

struct Robot: Greeter {
    let name: String
    // Robot overrides the default:
    func greet() -> String { return "BEEP. UNIT \(name)." }
}

print(Person(name: "Ada").greet())   // Hello, I'm Ada
print(Robot(name: "X1").greet())     // BEEP. UNIT X1.

4️⃣ Conforming to Multiple Protocols

A type can conform to several protocols, and a parameter can require a composition of them with & (e.g. Named & Aged).

// A type can conform to several protocols at once (comma-separated).
protocol Named { var name: String { get } }
protocol Aged  { var age: Int { get } }

struct User: Named, Aged {
    let name: String
    let age: Int
}

func describe(_ thing: Named & Aged) {
    print("\(thing.name) is \(thing.age)")
}

describe(User(name: "Sam", age: 30))   // Sam is 30

Your turn. Fill in the blanks to conform to a protocol and implement its method.

// 🎯 YOUR TURN — fill in the blanks, then run it in SwiftFiddle.
protocol Shape {
    func area() -> Double
}

// 1) Make Square conform to Shape. Replace ___ with the protocol name.
struct Square: ___ {        // 👉 replace ___ with 'Shape'
    let side: Double
    // 2) Provide the required method. Replace ___ with the keyword 'func'.
    ___ area() -> Double {  // 👉 replace ___ with 'func'
        return side * side
    }
}

let s = Square(side: 4)
print(s.area())   // 16.0

Pro Tips

Common Errors (and the fix)

📋 Quick Reference

TaskCodeNote
Defineprotocol P { func f() }requirements only
Adoptstruct S: P { … }after a colon
Read-only propvar x: Int { get }in protocol
Defaultextension P { func f() {…} }shared behaviour
ComposeA & Bneeds both

Mini-Challenge: Describable

No blanks this time — just a brief and an outline. Build it, run it, and check your output against the example in the comments.

// 🎯 MINI-CHALLENGE: a Describable protocol
// 1. Define a protocol Describable with a method describe() -> String.
// 2. Add a protocol extension giving a default describe() that returns
//    "An unknown thing".
// 3. Make a struct Book conform; override describe() to return its title.
// 4. Make a struct Mystery conform but DON'T override (uses the default).
// 5. Print both describe() results.
//
// ✅ Expected output:
//    Swift Basics
//    An unknown thing

// your code here

🎉 Lesson Complete!

Practice quiz

What is a protocol in Swift?

  • A concrete class
  • A kind of optional
  • A blueprint of requirements that a type can adopt
  • A global function

Answer: A blueprint of requirements that a type can adopt. A protocol defines a set of requirements (methods, properties) that conforming types must implement.

What keyword declares that a type adopts a protocol?

  • There's no keyword — you list it after a colon
  • implements
  • extends
  • uses

Answer: There's no keyword — you list it after a colon. You adopt a protocol by listing it after a colon: struct Dog: Animal { ... }.

Can a Swift type conform to multiple protocols?

  • No, only one
  • Only classes can
  • Only via inheritance
  • Yes, by separating them with commas

Answer: Yes, by separating them with commas. A type can conform to many protocols, listed comma-separated: struct X: A, B, C.

How do you require a read-only property in a protocol?

  • let name: String
  • var name: String { get }
  • func name() -> String only
  • var name: String { set }

Answer: var name: String { get }. Protocol properties are declared as var with { get } for read-only, or { get set } for read-write.

Can you use a protocol as a type for a variable?

  • Yes — a variable can be typed as the protocol, holding any conforming value
  • No, never
  • Only inside extensions
  • Only for classes

Answer: Yes — a variable can be typed as the protocol, holding any conforming value. A protocol can be used as a type, so a variable of that protocol type can hold any conforming instance.

How do you provide a DEFAULT implementation of a protocol method?

  • In the protocol body directly
  • In a subclass only
  • In a protocol extension
  • You can't — it must always be implemented

Answer: In a protocol extension. Default implementations live in a protocol extension, so conformers inherit them unless they override.

What does it mean for a struct to 'conform to' a protocol?

  • It inherits its stored properties
  • It provides implementations for all the protocol's requirements
  • It becomes a class
  • It copies the protocol's code

Answer: It provides implementations for all the protocol's requirements. Conforming means the type supplies implementations for every requirement the protocol declares.

Which built-in protocol lets you compare instances with ==?

  • Comparable
  • Hashable
  • Codable
  • Equatable

Answer: Equatable. Equatable provides ==. Comparable adds < ordering; Hashable enables use in sets/dictionaries.

Can a protocol require a method WITHOUT providing its body?

  • No
  • Yes — it declares the signature; conformers supply the body
  • Only for static methods
  • Only in classes

Answer: Yes — it declares the signature; conformers supply the body. A protocol declares method signatures only; conforming types provide the actual implementation.

What happens if a type claims conformance but misses a requirement?

  • It compiles with a warning
  • The requirement is auto-generated
  • It is a compile-time error
  • It runs but crashes

Answer: It is a compile-time error. Missing a required member is a compile-time error — the compiler enforces full conformance.

Continue this course

Frequently asked questions

What is a protocol in Swift, and how is it different from a class?

A protocol is a blueprint of requirements — methods, properties, and so on — that a type promises to provide. Unlike a class, a protocol contains no stored state and no implementation by itself (until you add a protocol extension). Structs, enums, and classes can all conform to protocols, and a type can conform to many at once.

How does a type adopt or conform to a protocol?

List the protocol after a colon in the type declaration, e.g. struct Dog: Animal, then implement every requirement the protocol declares. If you miss any requirement, the compiler reports an error. You can conform to several protocols by separating them with commas: struct X: A, B.

Can I use a protocol as a type?

Yes. A protocol can be used as the type of a variable, parameter, or array element, so it can hold any conforming value. For example [Animal] can store both a Dog and a Cat. In recent Swift you write 'any Animal' to make this existential type explicit.

How do I give protocol methods a default implementation?

Add a protocol extension and implement the method there. Every conforming type then gets that behavior for free and can override it by supplying its own implementation. This is the core technique behind protocol-oriented programming and lets you share code without inheritance.

What are some important built-in protocols?

Equatable enables ==, Comparable adds ordering with <, Hashable lets values be used in Sets and as Dictionary keys, and Codable enables encoding/decoding to formats like JSON. Many of these can be synthesized automatically by the compiler when your stored properties already conform.