Topics Covered in This iOS Development Tutorial:
Defining a Protocol
Tutorial Learning Path
Protocol Definition
Learn how to define protocols using the protocol keyword and establish method signatures
Delegate Implementation
Understand how to create delegate properties and establish communication between classes
Protocol Conformance
Implement required methods to make classes conform to defined protocols
Testing Integration
Run and debug the implementation to see protocol delegation in action
Exercise Overview
A protocol is a fundamental building block of iOS development—defining a contract of required and optional methods or properties that classes can implement and adopt. Think of protocols as architectural blueprints that ensure consistent communication between objects in your application. In this exercise, you'll implement a custom protocol using our Car class, learning how protocols enable the delegate pattern that powers much of iOS development, from table views to network callbacks.
A protocol is a set of required and optional methods or properties that another class can implement, or adopt. Think of a protocol as a blueprint or contract that a class adheres to.Protocol vs Class Implementation
| Feature | Protocol | Class |
|---|---|---|
| Purpose | Defines contract | Provides implementation |
| Instantiation | Cannot instantiate | Can create objects |
| Methods | Signatures only | Full implementation |
| Inheritance | Multiple adoption | Single inheritance |
Getting Started
Launch Xcode if it isn't already open.
Go to File > Open.
Navigate to Desktop > Class Files > yourname-iOS App Dev 1 Class > Car Setup Done and double–click on Car.xcodeproj.
This code builds on our previous exercise, but notice we've now separated the Car and Mustang classes into distinct files—a best practice that improves code organization and maintainability as your projects scale. You'll see both files clearly listed in the Project navigator.
Project Setup Checklist
Ensure you have the latest version for best compatibility
Use the file menu to access existing projects
Find the project in Desktop > Class Files > yourname-iOS App Dev 1 Class > Car Setup Done
Check that Car and Mustang classes are in separate files in Project navigator
This exercise builds on previous work but uses separate files for Car and Mustang classes, demonstrating better code organization practices.
Defining a Protocol
Protocols in Swift are declared using the protocol keyword, similar to how you define classes and structs. However, protocols serve a different purpose—they establish blueprints containing properties and methods that conforming types must adopt. When a class implements all required protocol methods, it conforms to that protocol, enabling powerful design patterns like delegation.
In the Project navigator, select Car.swift.
Between the Engine structure and the Car class, around line 16, implement a protocol by typing the bold code:
struct Engine { var type = "" var horsePower = 0 } protocol CarDelegate { } class Car {This introduces a delegate pattern—one of iOS development's most essential design patterns. Delegation allows objects to hand off specific responsibilities to other objects, promoting loose coupling and reusable code. You'll encounter this pattern throughout iOS frameworks, from UITableViewDelegate to networking completion handlers.
Now we need to define the methods that comprise our delegation contract. Protocols contain only method signatures and property declarations—they specify requirements without implementation details. Add the following methods as shown in bold:
protocol CarDelegate { func starting() func didStart() }These methods establish a communication protocol for tracking car state changes. The naming convention follows iOS standards:
starting()indicates an action about to occur, whiledidStart()confirms completion. This pattern enables responsive user interfaces that can react to state changes—for instance, showing a loading spinner during startup and updating the UI when the process completes.In the Project navigator, select ViewController.swift.
At the top, add the bold code:
class ViewController: UIViewController, CarDelegate {A red alert
will appear to the right of the line you just added. Click it.The error message reads: Type 'ViewController
'does not conform to protocol 'CarDelegate'This compile-time error demonstrates Swift's protocol safety. When a class declares conformance to a protocol, the compiler enforces that all required methods are implemented. This prevents runtime crashes and ensures your delegation contracts are fully satisfied—a significant advantage over Objective-C's optional protocol methods.
To resolve this error and complete our protocol conformance, add the required method implementations using the bold code shown:
//DELEGATE METHODS func starting() { print("Car is starting. Display UI for user to indicate car is starting.") } func didStart() { print("Car did start. Add UI to let user know car has started.") }In the Project navigator, return to Car.swift.
Now that our protocol is defined, let's integrate it into our Car class. Create a delegate property with the CarDelegate type, noting the optional declaration that prevents retain cycles:
var engine: Engine var delegate: CarDelegate? init(speed: Int, mpg: Float) {Within the start method, around line 36, add the bold code:
func start() { self.speed = 10 self.delegate?.starting() print("Vroom") }The optional chaining syntax (
?.) safely calls the delegate method only if a delegate is assigned. This prevents crashes while maintaining clean separation of concerns—the Car class focuses on its core functionality while delegating UI responsibilities to the appropriate controller.In the Project navigator, select ViewController.swift.
Establish the delegation relationship by adding the bold code:
override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. mustang.delegate = self mustang.start() }This assignment completes our delegation chain. The mustang instance (inheriting from Car) will now notify the ViewController of state changes through our protocol methods. This enables the view controller to update the user interface appropriately while keeping the Car class focused on business logic rather than UI concerns.
Return to Car.swift.
Complete the delegation cycle by adding the completion callback:
func start() { self.speed = 10 self.delegate?.starting() print("Vroom") self.delegate?.didStart() }Time to test our protocol implementation! At the top right, click the Show the Debug area button
if it's not already visible.At the top left of Xcode, click Stop
then click the Run button
.The simulator will launch, displaying a blank white screen. The real action is happening behind the scenes in our protocol implementation.
Return to the main Xcode window and examine the Debug area. You should see the complete delegation sequence in the console output:
The engine is revving Car is starting. Display UI for user to indicate car is starting. Vroom Car did start. Add UI to let user know car has started.
Save your work and keep Xcode open—we'll build upon this protocol foundation in the next exercise.
Protocol Implementation Process
Declare Protocol
Use the protocol keyword between Engine struct and Car class around line 16
Add Method Signatures
Define starting() and didStart() methods within the CarDelegate protocol
Adopt Protocol
Make ViewController conform to CarDelegate by adding it to the class declaration
Implement Methods
Add concrete implementations of starting() and didStart() methods in ViewController
Create Delegate Property
Add optional CarDelegate property to Car class for communication
Connect Delegate
Set mustang.delegate = self in viewDidLoad to establish the connection
When adopting a protocol, you must implement ALL required methods. Missing implementations will cause compile-time errors.
Delegation Pattern Benefits
Loose Coupling
Objects communicate without direct dependencies. Changes to one class don't require changes to others.
Reusability
Multiple classes can adopt the same protocol. Promotes code reuse and consistent interfaces.
Testability
Easy to create mock objects for testing. Protocols enable dependency injection and unit testing.
Code Execution Flow
App Launch
viewDidLoad sets mustang.delegate = self
Start Method Called
mustang.start() triggers in viewDidLoad
Starting Callback
delegate?.starting() calls ViewController method
Completion Callback
delegate?.didStart() notifies process completion