Protocols & Extensions
Protocols and Extensions are core to Swift’s "Protocol-Oriented Programming" paradigm.
Protocols
A Protocol defines a blueprint of methods, properties, and other requirements. It doesn't provide the code itself, but it ensures that any type that adopts it will have certain features.
protocol Identifiable {
// { get set } means the property must be readable and writable
// { get } would mean it only needs to be readable
var id: String { get set }
}
struct User: Identifiable {
var id: String // Conforms to Identifiable by providing 'id'
var name: String
}Why use Protocols?
Adopting a protocol like Identifiable allows you to write generic code that works with any type as long as it has an id. For example, a function could accept a list of "Identifiable" items and print their IDs, regardless of whether they are Users, Products, or Books.
Extensions
Extensions add new functionality to an existing class, struct, enum, or protocol type. This includes types for which you do not have the original source code (like built-in types).
extension Int {
func squared() -> Int {
self * self
}
}
print(7.squared()) // 49Protocol Extensions
You can extend a protocol to provide a default implementation to all conforming types. This is incredibly powerful as it adds functionality to multiple types at once.
extension Identifiable {
func displayID() {
print("The ID is: \(id)")
}
}
// Usage Example:
let user = User(id: "USR-01", name: "Pirate")
user.displayID() // "The ID is: USR-01" -> User got this method for free!Extensions vs. Inheritance
While both allow you to add functionality, they serve different purposes:
| Feature | Inheritance (Classes Only) | Extensions (All Types) |
|---|---|---|
| Relationship | "Is-a": Creates a child subclass. | "Skills": Adds a new ability to existing type. |
| Storage | Can add new Stored Properties. | Cannot add Stored Properties. |
| Override | Can change parent behavior via override. | Can only add new behavior. |
Note: Extensions are the preferred way to grow your code in Swift. They keep things simple by adding "skills" without creating a messy "family tree" (inheritance).
Built-in Protocols
Swift has many powerful built-in protocols that you can adopt to make your custom types feel like native ones. Some of them are:
Equatable
Allows you to compare two instances using ==.
struct Point: Equatable {
var x: Int, y: Int
}
let a = Point(x: 1, y: 2)
let b = Point(x: 1, y: 2)
print(a == b) // trueCustomStringConvertible
Allows you to customize how your type is printed in print() or converted to a String.
struct Pirate: CustomStringConvertible {
var name: String
var description: String { "Ahoy! I am \(name)." }
}
let p = Pirate(name: "Jack")
print(p) // "Ahoy! I am Jack."Comparable
Allows you to use operators like <, >, and sorting.
struct Score: Comparable {
var value: Int
static func < (lhs: Score, rhs: Score) -> Bool {
lhs.value < rhs.value
}
}
let high = Score(value: 100)
let low = Score(value: 50)
print(low < high) // true