Protocols in Swift
Introduction to Protocols in Swift
A protocol in Swift defines a blueprint of methods, properties, and other requirements that a class, structure (struct), or enumeration (enum) must follow. It ensures that different types conform to the same set of behaviors, promoting code consistency, modularity, and reusability.
Swift protocols are widely used in protocol-oriented programming (POP), a key paradigm in Swift that emphasizes composition over inheritance.
Key Features of Protocols
- Define required methods and properties.
- Adopted by classes, structs, and enums.
- Support default implementations via protocol extensions.
- Enable multiple inheritance in Swift.
- Used in delegation, abstraction, and decoupling.
1. Basic Protocol Syntax
protocol Greetable {
var name: String { get } // Read-only property
func greet() -> String
}
The Greetable
protocol defines a name
property and a greet()
method.
Any type conforming to this protocol must implement these requirements.
2. Conforming to a Protocol
A struct implementing the Greetable
protocol:
struct Person: Greetable {
var name: String
func greet() -> String {
return "Hello,
my name is \(name)."
}
}
let person = Person(name: "Alice")
print(person.greet())
Output
3. Using Protocols with Classes
A class can also conform to a protocol:
class Animal: Greetable {
var name: String
init(name: String) {
self.name = name
}
func greet() -> String {
return "I am a
\(name)."
}
}
let dog = Animal(name: "Dog")
print(dog.greet())
Output
4. Protocols with Mutating Methods
When using mutating methods inside a struct or enum, you must mark them as mutating
in the
protocol.
protocol Toggleable {
mutating func toggle()
}
struct LightSwitch: Toggleable {
var isOn = false
mutating func toggle() {
isOn.toggle()
}
}
var switch1 = LightSwitch()
print(switch1.isOn) // false
switch1.toggle()
print(switch1.isOn) // true
Output
true
5. Protocols with Computed Properties
A protocol can define computed properties, but they must be marked as { get }
or
{ get set }
.
protocol Describable {
var description: String { get }
}
struct Car: Describable {
var model: String
var description: String {
return "Car
Model: \(model)"
}
}
let myCar = Car(model: "Tesla Model S")
print(myCar.description)
Output
6. Protocol Inheritance
A protocol can inherit from one or more protocols.
protocol Vehicle {
var speed: Int { get set }
}
protocol Electric {
var batteryLevel: Int { get }
}
protocol ElectricCar: Vehicle, Electric {}
struct Tesla: ElectricCar {
var speed: Int
var batteryLevel: Int
}
let myTesla = Tesla(speed: 120, batteryLevel: 85)
print("Speed: \(myTesla.speed), Battery:
\(myTesla.batteryLevel)%")
Output
7. Protocols with Default Implementations
A protocol extension can provide a default implementation.
protocol Drawable {
func draw()
}
extension Drawable {
func draw() {
print("Default
drawing action")
}
}
struct Circle: Drawable {}
struct Square: Drawable {
func draw() {
print("Drawing a
square")
}
}
let circle = Circle()
circle.draw() // Uses default implementation
let square = Square()
square.draw() // Uses custom implementation
Output
Drawing a square
8. Protocols with Delegation
Protocols are commonly used for delegation in Swift.
protocol DownloadDelegate {
func didFinishDownload()
}
class Downloader {
var delegate: DownloadDelegate?
func startDownload() {
print("Downloading...")
delegate?.didFinishDownload()
}
}
class ViewController: DownloadDelegate {
func didFinishDownload() {
print("Download
completed successfully!")
}
}
let vc = ViewController()
let downloader = Downloader()
downloader.delegate = vc
downloader.startDownload()
Output
Download completed successfully!
9. Associated Types in Protocols
Protocols can define generic placeholders using associatedtype
.
protocol Container {
associatedtype Item
func add(_ item: Item)
func getAllItems() -> [Item]
}
struct IntContainer: Container {
private var items: [Int] = []
func add(_ item: Int) {
items.append(item)
}
func getAllItems() -> [Int] {
return items
}
}
var numbers = IntContainer()
numbers.add(5)
numbers.add(10)
print(numbers.getAllItems())
Output
10. Type Constraints in Protocols
You can constrain a protocol to work with specific types.
protocol Summable {
associatedtype Number: Numeric
func sum(_ a: Number, _ b: Number) -> Number
}
struct Calculator: Summable {
func sum(_ a: Int, _ b: Int) -> Int {
return a + b
}
}
let calc = Calculator()
print(calc.sum(5, 10))
Output
Best Practices for Using Protocols
- Use protocols to define behaviors that multiple types can adopt.
- Leverage protocol extensions to add default implementations.
- Prefer protocol-oriented programming over class inheritance.
- Use protocols for delegation to improve code reusability.
- Use associated types for flexible generic programming.
Conclusion
Protocols in Swift enable flexible and reusable designs by defining standardized behaviors across different types. Whether used for protocol-oriented programming, delegation, or generic programming, protocols make Swift applications more modular, scalable, and maintainable.