Understanding Protocol-Delegate Pattern in Swift with Real Examples
Table of Contents
- Ⅰ. Introduction
- Ⅱ. What is a Protocol?
- Ⅲ. Why Use Protocols?
- Ⅳ. What is a Delegate?
- Ⅴ. How to Use the Protocol-Delegate Pattern in Swift
- Ⅵ. Protocol-Delegate Pattern in UIKit with Real Examples
- Ⅶ. Why Use the Protocol-Delegate Pattern in Swift?
- Ⅷ. Summary
- Ⅸ. References
Ⅰ.Introduction
One of the most powerful and elegant patterns in iOS development is the protocol-delegate mechanism. It enables flexible communication between objects without creating tight coupling — a core principle in clean architecture.
Ⅱ.What is a Protocol?
In Swift, a protocol is like an interface — it defines a set of methods or properties that a conforming type must implement.
A Swift Protocol is Swift’s version of an Interface in object-oriented programming (OOP). If you’re new to OOP, think of an interface as a blueprint that any class, struct, or enum adopting it must follow.
For example, imagine a Vehicle interface that declares a function startEngine(). In Swift, any type conforming to the Vehicle protocol must implement startEngine(). This is known as Protocol Conformance.
1 | // Swift code example |
Ⅲ.Why Use Protocols?
Swift protocols establish contracts between different components without exposing unnecessary details. For instance, a vehicle’s user doesn’t need to know how an ATV or spaceship starts its engine—just that they can call startEngine().
This is where protocols come in: they let us define a series of properties and methods that we want to use. They don’t implement those properties and methods – they don’t actually put any code behind it – they just say that the properties and methods must exist, a bit like a blueprint.
For example
Think about how we might write some code to simulate someone commuting from their home to their office. We might create a small Car,train ,busstruct,or they might use a bike, or any number of other transport options.
Now, each transport option behaves differently:
- A train is fast but limited to tracks.
- A bike is slow but flexible.
- A car is decent, but might get stuck in traffic.
In real-world development, without protocols, we would likely need to write different commute functions for each type of transportation, each requiring different parameters and implementations. For example, each type of transportation might have its own way of calculating time and how to perform the actual act of moving.
As a result, the code would become cumbersome and hard to maintain. You might end up writing multiple functions like this:
1 | func commuteByCar(distance: Int) { ... } |
Now imagine if you later want to add a plane, subway, or electric scooter — your code will get messy quickly.Consider the example , without protocols, we might have to write separate functions for each transportation mode
Instead of writing separate functions for each transport type, you can define a protocol to describe what all vehicles have in common:
1 | protocol Vehicle { |
This protocol acts like a contract. It doesn’t care how a vehicle works — it just says, “You must be able to estimate time and travel a given distance.”
Now we can make a Car conform to this protocol:
1 | struct Car: Vehicle { |
We can do the same for a Bike:
1 | struct Bike: Vehicle { |
Now Here’s the Magic:
We can now write one universal function that works with any vehicle that conforms to Vehicle:
1 | func commute(distance: Int, using vehicle: Vehicle) { |
Usage:
1 | let car = Car() |
- The protocol Vehicle is like a blueprint or pass — as long as a type follows the rules (implements the required methods), it can be passed into our commute function.
- We don’t care which vehicle is being used. We only care that it knows how to estimate time and move.
- This makes our code cleaner, easier to read, and more scalable.
- If we want to add a Plane or Subway, we don’t need to change commute() at all — just make them conform to Vehicle.
Ⅳ.What is a Delegate?:
Think of a delegate as someone you appoint to make a decision or perform a task on your behalf.
Imagine you’re not commuting alone. You have a personal assistant — someone you trust to choose the best vehicle for your daily commute.
You don’t choose the car, bus, or bike yourself.
You say to your assistant:
“Here’s the distance. You decide which vehicle to use, and let me know how long it will take.”
That assistant is your delegate.
In Programming Terms:
A delegate is a reference to another object that responds to certain events or provides custom behavior.
You (the main object) define what needs to happen,
but you let someone else (the delegate) decide how exactly it should happen.
This pattern:
- Keeps your code decoupled
- Allows customization
- Follows the protocol contract for communication
Ⅳ.Why Use Delegate?
Let’s Build a Transportation Example with Delegate
Step 1: Define a Delegate Protocol:
1 | protocol CommuteDelegate { |
This says:
“If you’re going to be my assistant (delegate), you must tell me which vehicle you chose.”
Step 2: Define a Commuter Class That Uses the Delegate:
1 | class Commuter { |
Step 3: Create a Delegate That Implements the Decision Logic:
1 | class Assistant: CommuteDelegate { |
Step 4: Use It All Together:
1 | let commuter = Commuter() |
Output:
1 | Commuter: Asking delegate to choose vehicle... |
Key Takeaways
Concept | Analogy | Swift Term |
---|---|---|
delegate |
Your assistant | Another object you trust to do part of the job |
protocol |
Rules for being your assistant | Contract they must follow |
delegate property |
The assistant you hired | delegate variable |
delegate method |
“Choose the vehicle” | didChooseVehicle() |
A delegate is an object you assign to perform tasks or respond to events on your behalf.
It must conform to a protocol, which defines what methods it must implement.
This allows custom logic to be injected without hard-coding it into the main object — keeping code flexible, reusable, and clean.
Ⅴ.How to Use the Protocol-Delegate Pattern in Swift
The Protocol-Delegate pattern is a fundamental communication technique in Swift. It allows one object to delegate responsibility to another, promoting loose coupling, modularity, and code reuse.
Here’s a general step-by-step guide to using it in any Swift project:
1. Define a Protocol
1 | protocol MyDelegate: AnyObject { |
AnyObject is used to restrict the protocol to class types, which is important for weak references.
2. Add a Delegate Property
In the “sender” (the class that needs to report back), declare a delegate property.
1 | class TaskPerformer { |
weak is essential to avoid retain cycles (especially when used in UIViewControllers).
3. Conform to the Protocol
In the “receiver” (usually a controller), conform to the protocol and implement the required methods.
1 | class ViewController: UIViewController, MyDelegate { |
4. Set the Delegate
1 | let performer = TaskPerformer() |
This pattern acts like a bridge between performTask() or didCompleteTask(), allowing them to communicate indirectly.
The delegate serves as a messenger, informing each side when to respond or transition.
Ⅵ.Protocol-Delegate Pattern in UIkit with Real Examples
Example 1: RingViewDelegate – Custom View Delegation
I created a custom view called RingView that animates a workout progress ring. However, RingView should not be responsible for what happens after the animation ends.
So I declared a protocol:
1 | protocol RingViewDelegate: AnyObject { |
In the controller:
1 | class WorkoutViewController: UIViewController { |
This separates UI logic from business logic, making both components reusable.
Example 2: WorkoutDelegate – View Controller Communication
When the workout ends, I present a results page:
1 | performSegue(withIdentifier: "WorkoutResultsSegue", sender: nil) |
But how does the WorkoutResultsViewController tell the previous controller “Hey, I’m done”?
Again, with a delegate:
1 | protocol WorkoutDelegate: AnyObject { |
1 | class WorkoutViewController: UIViewController, WorkoutDelegate { |
Then in WorkoutResultsViewController, I use it like this:
1 | @IBAction func submitButtonTapped(_ sender: Any) { |
This is a perfect example of loosely coupled controller-to-controller communication.
In real-world iOS development, you should never assume a strict dependency between two view controllers — that one must always appear before the other, or that they must know each other’s internal structure.
Instead, using a delegate is like assigning a messenger between the two. The delegate handles communication without requiring a tight bond between the sender and receiver.
The protocol-delegate pattern isn’t just a nice-to-have in Swift — it’s a fundamental building block for writing maintainable, modular apps.
Ⅶ.Why Use the Protocol-Delegate Pattern in Swift?
1. Decoupling Logic for Flexibility
When you use a delegate, the object sending the message doesn’t need to know who receives it or how it will respond. This promotes loose coupling — a design principle that keeps your components flexible and easier to change.
Your views don’t need to know your controllers. Your controllers don’t need to hardcode downstream effects.
2. Promotes Code Reuse
By defining behaviors in protocols, you can swap out the delegate object at any time — as long as it conforms. This makes your views, components, and logic reusable across different parts of your app.
3. Encourages Clean Architecture
Separating what needs to happen (protocol) from how it happens (delegate implementation) allows you to follow the Single Responsibility Principle.
This separation keeps your views lightweight, your business logic testable, and your app architecture scalable.
4.Real-world Use Cases
This pattern is used all over the UIKit framework:
- UITableViewDelegate
- UICollectionViewDelegate
- CLLocationManagerDelegate
- URLSessionDelegate
Even Apple builds their frameworks around this principle — and for good reason.
Ⅷ.Summary
Protocols define the “what,” delegates provide the “how.”
The protocol-delegate pattern helps you build efficient, testable, and modular iOS apps by enabling components to talk to each other without tightly binding them together.
Whether you’re managing custom views, communicating between view controllers, or building scalable architecture — mastering this pattern will make you a better Swift developer.
Ⅸ.References
- Apple Developer Documentation. Protocols
- Swift Delegate: An In-Depth Guide for Structured iOS Programming. Swift Delegate: An In-Depth Guide for Structured iOS Programming
- A Step-by-Step Guide to the Delegate Pattern in Swift. https://medium.com/@afonso.script.sol/a-step-by-step-guide-to-the-delegate-pattern-in-swift-91a28de1baf8
- SwiftにおけるDelegateとは何か、なぜ使うのかSwiftにおけるDelegateとは何か、なぜ使うのか