Protocols and extensions are powerful Swift features that enable protocol-oriented programming and code reuse. Master these patterns for cleaner, more maintainable code.
Protocols
Basic Protocol
swift
protocol Drawable {
func draw()
var color: String { get }
}
struct Circle: Drawable {
let color: String = "blue"
func draw() {
print("Drawing a \(color) circle")
}
}
struct Square: Drawable {
let color: String = "red"
func draw() {
print("Drawing a \(color) square")
}
}
let shapes: [Drawable] = [Circle(), Square()]
for shape in shapes {
shape.draw()
}Protocol with Associated Types
swift
protocol Container {
associatedtype Item
mutating func add(_ item: Item)
var count: Int { get }
func get(_ index: Int) -> Item
}
struct Stack: Container {
typealias Item = Int
private var items: [Item] = []
mutating func add(_ item: Item) {
items.append(item)
}
var count: Int {
items.count
}
func get(_ index: Int) -> Item {
items[index]
}
}Protocol Inheritance
swift
protocol Vehicle {
var name: String { get }
func start()
}
protocol ElectricVehicle: Vehicle {
var batteryLevel: Int { get }
func charge()
}
struct Tesla: ElectricVehicle {
let name: String
var batteryLevel: Int = 100
func start() {
print("\(name) starting...")
}
func charge() {
print("Charging...")
batteryLevel = 100
}
}Extensions
Basic Extension
swift
extension Int {
var isEven: Bool {
self % 2 == 0
}
var isOdd: Bool {
!isEven
}
func squared() -> Int {
self * self
}
}
let number = 5
print(number.isEven) // false
print(number.isOdd) // true
print(number.squared()) // 25Extension with Initializers
swift
struct Temperature {
let celsius: Double
init(celsius: Double) {
self.celsius = celsius
}
}
extension Temperature {
init(fahrenheit: Double) {
self.celsius = (fahrenheit - 32) * 5/9
}
init(kelvin: Double) {
self.celsius = kelvin - 273.15
}
var fahrenheit: Double {
celsius * 9/5 + 32
}
var kelvin: Double {
celsius + 273.15
}
}
let temp1 = Temperature(celsius: 25)
let temp2 = Temperature(fahrenheit: 77)
let temp3 = Temperature(kelvin: 298.15)Protocol-Oriented Programming
Protocol Extensions
swift
protocol Summable {
func sum() -> Int
}
extension Array: Summable where Element == Int {
func sum() -> Int {
reduce(0, +)
}
}
let numbers = [1, 2, 3, 4, 5]
print(numbers.sum()) // 15Default Implementations
swift
protocol Describable {
var name: String { get }
func describe() -> String
}
extension Describable {
func describe() -> String {
return "This is \(name)"
}
}
struct Person: Describable {
let name: String
}
struct Car: Describable {
let name: String
func describe() -> String {
return "This is a \(name) car"
}
}
let person = Person(name: "John")
let car = Car(name: "Tesla")
print(person.describe()) // "This is John"
print(car.describe()) // "This is a Tesla car"Powerful Patterns
Equatable Implementation
swift
struct User {
let id: String
let name: String
let email: String
}
extension User: Equatable {
static func == (lhs: User, rhs: User) -> Bool {
lhs.id == rhs.id
}
}Comparable Implementation
swift
struct Task {
let title: String
let priority: Int
let completed: Bool
}
extension Task: Comparable {
static func < (lhs: Task, rhs: Task) -> Bool {
if lhs.priority != rhs.priority {
return lhs.priority < rhs.priority
}
return lhs.title < rhs.title
}
}
let tasks = [
Task(title: "Low", priority: 3, completed: false),
Task(title: "High", priority: 1, completed: false),
Task(title: "Medium", priority: 2, completed: false)
]
let sortedTasks = tasks.sorted()Hashable for Custom Types
swift
struct UserProfile {
let id: String
let username: String
let email: String
}
extension UserProfile: Hashable {
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
static func == (lhs: UserProfile, rhs: UserProfile) -> Bool {
lhs.id == rhs.id
}
}
let userProfiles: Set<UserProfile> = [
UserProfile(id: "1", username: "user1", email: "user1@example.com"),
UserProfile(id: "2", username: "user2", email: "user2@example.com")
]Conditional Conformance
swift
struct Wrapper<T> {
let value: T
}
extension Wrapper: Equatable where T: Equatable {
static func == (lhs: Wrapper, rhs: Wrapper) -> Bool {
lhs.value == rhs.value
}
}
extension Wrapper: Hashable where T: Hashable {
func hash(into hasher: inout Hasher) {
hasher.combine(value)
}
}
let wrapper1 = Wrapper(value: "hello")
let wrapper2 = Wrapper(value: "hello")
print(wrapper1 == wrapper2) // trueOpaque Types
swift
protocol Shape {
func area() -> Double
}
struct Rectangle: Shape {
let width: Double
let height: Double
func area() -> Double {
width * height
}
}
struct Circle: Shape {
let radius: Double
func area() -> Double {
.pi * radius * radius
}
}
struct ShapeFactory {
func makeShape() -> some Shape {
Rectangle(width: 10, height: 5)
}
}
let factory = ShapeFactory()
let shape = factory.makeShape()
print(shape.area())Extension on Standard Library
String Extensions
swift
extension String {
var isValidEmail: Bool {
let emailRegex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
let predicate = NSPredicate(format: "SELF MATCHES %@", emailRegex)
return predicate.evaluate(with: self)
}
var isNumeric: Bool {
Double(self) != nil
}
func truncate(_ length: Int, trailing: String = "...") -> String {
if self.count <= length {
return self
}
return String(self.prefix(length)) + trailing
}
}
let email = "user@example.com"
print(email.isValidEmail) // trueDate Extensions
swift
extension Date {
var formattedDate: String {
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .none
return formatter.string(from: self)
}
var isToday: Bool {
Calendar.current.isDateInToday(self)
}
var isYesterday: Bool {
Calendar.current.isDateInYesterday(self)
}
func adding(days: Int) -> Date {
Calendar.current.date(byAdding: .day, value: days, to: self) ?? self
}
}
let today = Date()
print(today.formattedDate)
print(today.isToday)Best Practices
- 1Protocol-oriented: Use protocols to define contracts
- 2Default implementations: Provide sensible defaults
- 3Conditional conformance: Add conformances conditionally
- 4Extensions: Extend types, don't subclass
- 5Opaque types: Hide implementation details
- 6Hashable: Implement for custom types
- 7Equatable: Custom equality when needed
- 8Codable: Make types serializable
Protocols and extensions enable flexible, reusable Swift code!