Swift Protocol: Syntax, Usage, and Examples
A Swift protocol defines a blueprint of methods, properties, and other requirements that classes, structures, and enumerations must adopt. Protocols let you define shared functionality without requiring inheritance, making them a key part of protocol-oriented programming in Swift.
How to Use Protocols in Swift
To define a protocol, use the protocol
keyword, then list the required properties and methods. Any type that conforms to the protocol must implement these requirements.
swift
protocol Greetable {
var name: String { get }
func greet()
}
struct Person: Greetable {
var name: String
func greet() {
print("Hello, my name is \(name).")
}
}
let john = Person(name: "John")
john.greet() // Output: Hello, my name is John.
Here, Greetable
is a Swift protocol that requires a name
property and a greet
method. The Person
struct conforms to the protocol by implementing both.
Protocol Properties
Protocols define protocol properties in Swift that conforming types must provide. These can be read-only ({ get }
) or read-write ({ get set }
).
swift
protocol Vehicle {
var speed: Int { get set }
var capacity: Int { get }
}
Protocol Methods
Protocols can define methods without providing an implementation. Any type conforming to the protocol must implement these methods.
swift
protocol Drivable {
func start()
func stop()
}
class Car: Drivable {
func start() {
print("Car is starting...")
}
func stop() {
print("Car is stopping...")
}
}
When to Use Protocols in Swift
Standardizing Behavior
Protocols help define a standard behavior across multiple types without forcing them into a class hierarchy.
swift
protocol Flyable {
func fly()
}
struct Bird: Flyable {
func fly() {
print("The bird is flying.")
}
}
struct Plane: Flyable {
func fly() {
print("The plane is flying.")
}
}
Creating Flexible Code
You can use protocol-oriented programming in Swift to make your code more flexible by defining shared behavior that multiple unrelated types can adopt.
swift
protocol Storable {
func save()
}
class Document: Storable {
func save() {
print("Saving document...")
}
}
class Image: Storable {
func save() {
print("Saving image...")
}
}
Supporting Delegation
Protocols are commonly used for delegation, allowing one object to pass responsibility to another.
swift
protocol DownloadDelegate {
func didFinishDownloading()
}
class Downloader {
var delegate: DownloadDelegate?
func download() {
print("Downloading...")
delegate?.didFinishDownloading()
}
}
class ViewController: DownloadDelegate {
func didFinishDownloading() {
print("Download complete!")
}
}
let vc = ViewController()
let downloader = Downloader()
downloader.delegate = vc
downloader.download()
// Output:
// Downloading...
// Download complete!
Examples of Protocols in Swift
Using a Protocol with an Array
You can store different types in an array as long as they conform to the same protocol.
swift
protocol Animal {
func makeSound()
}
struct Dog: Animal {
func makeSound() {
print("Woof!")
}
}
struct Cat: Animal {
func makeSound() {
print("Meow!")
}
}
let animals: [Animal] = [Dog(), Cat()]
animals.forEach { $0.makeSound() }
// Output:
// Woof!
// Meow!
Extending Protocols
A Swift protocol extension allows you to add default implementations for protocol methods.
swift
protocol Identifiable {
var id: String { get }
}
extension Identifiable {
func identify() {
print("My ID is \(id).")
}
}
struct User: Identifiable {
var id: String
}
let user = User(id: "123")
user.identify() // Output: My ID is 123.
Using Generics with Protocols
A Swift generic protocol allows you to define flexible and reusable code that works with multiple types.
swift
protocol Storage {
associatedtype Item
func store(_ item: Item)
}
class Box<T>: Storage {
typealias Item = T
func store(_ item: T) {
print("Storing \(item)")
}
}
let intBox = Box<Int>()
intBox.store(42) // Output: Storing 42
Protocol Inheritance
A protocol can inherit from one or more protocols using a comma-separated list.
swift
protocol Movable {
func move()
}
protocol Stoppable {
func stop()
}
protocol Vehicle: Movable, Stoppable {}
class Bicycle: Vehicle {
func move() {
print("Bicycle is moving.")
}
func stop() {
print("Bicycle is stopping.")
}
}
let bike = Bicycle()
bike.move() // Output: Bicycle is moving.
bike.stop() // Output: Bicycle is stopping.
Learn More About Protocols in Swift
Class-Only Protocols
You can restrict a protocol to class-only types using AnyObject
.
swift
protocol ViewControllerDelegate: AnyObject {
func didUpdate()
}
Optional Protocol Methods
Swift doesn’t support optional protocol methods directly, but you can work around this using @objc
and optional
.
swift
@objc protocol OptionalProtocol {
@objc optional func optionalMethod()
}
Protocol vs. Class
- Protocols allow multiple types to share behavior without inheritance.
- Classes enforce a strict hierarchy where child classes inherit from a parent.
- Use protocols when defining behavior that multiple unrelated types should conform to.
- Use classes when modeling relationships where objects need shared state and behavior.
Protocol-Oriented Programming vs. Object-Oriented Programming
- Object-Oriented Programming (OOP) relies on classes and inheritance.
- Protocol-Oriented Programming (POP) promotes composition over inheritance, leading to more modular and testable code.
Using Protocols in Real-World Projects
Protocols are widely used in SwiftUI, UIKit, and networking. You often see them in delegate patterns, custom UI components, and dependency injection.
Example: Networking with a Protocol
swift
protocol NetworkService {
func fetchData(from url: String)
}
class APIClient: NetworkService {
func fetchData(from url: String) {
print("Fetching data from \(url)")
}
}
let apiClient: NetworkService = APIClient()
apiClient.fetchData(from: "https://example.com")
// Output: Fetching data from https://example.com
Best Practices for Using Protocols in Swift
- Use protocols instead of inheritance when possible. They make your code more flexible and reusable.
- Define small, focused protocols. Avoid protocols with too many responsibilities.
- Use protocol extensions to provide default implementations.
- Avoid forcing class inheritance unless necessary. Use protocol-oriented programming when structuring your app.
- Prefer composition over inheritance. Instead of creating deep class hierarchies, break functionality into separate protocols.