Lesson 79Protocols
Common Protocols
Swift Standard Library Protocols
These protocols are used throughout Swift and its standard library. Conforming to them unlocks powerful functionality!
Essential Protocols
Equatable
Compare with == and !=
Comparable
Compare with <, >, sort()
Hashable
Use in Set, Dictionary keys
Codable
JSON/Plist encoding
Identifiable
Unique identity for SwiftUI
CustomStringConvertible
Custom print() output
Collection Protocols
Sequence - for-in loop support
Collection - index-based access
MutableCollection - element modification
RandomAccessCollection - O(1) index access
Codable Deep Dive
Codable = Encodable & Decodable
- Auto-synthesized for simple types
- Use CodingKeys for custom JSON keys
- Configure encoder/decoder for dates, etc.
Auto-Synthesis
Swift auto-generates conformance when all properties conform:
- Equatable: All properties are Equatable
- Hashable: All properties are Hashable
- Codable: All properties are Codable
main.swift
// COMMON SWIFT PROTOCOLS
// Essential protocols you'll use frequently
// EQUATABLE - Compare for equality
struct Point: Equatable {
var x: Int
var y: Int
// Swift auto-generates == for simple types
}
let p1 = Point(x: 1, y: 2)
let p2 = Point(x: 1, y: 2)
print(p1 == p2) // true
// Custom Equatable
struct Person: Equatable {
var id: Int
var name: String
static func == (lhs: Person, rhs: Person) -> Bool {
return lhs.id == rhs.id // Compare by ID only
}
}
// COMPARABLE - Compare for ordering
struct Score: Comparable {
var value: Int
var player: String
static func < (lhs: Score, rhs: Score) -> Bool {
return lhs.value < rhs.value
}
}
let scores = [Score(value: 100, player: "A"), Score(value: 50, player: "B")]
let sorted = scores.sorted() // Uses <
// HASHABLE - Use in Sets and as Dictionary keys
struct User: Hashable {
var id: Int
var name: String
func hash(into hasher: inout Hasher) {
hasher.combine(id) // Hash by ID
}
}
var userSet: Set<User> = []
userSet.insert(User(id: 1, name: "Alice"))
var userScores: [User: Int] = [:]
userScores[User(id: 1, name: "Alice")] = 100
// CODABLE - JSON encoding/decoding
struct Article: Codable {
var title: String
var author: String
var publishDate: Date
}
// Encode to JSON
let article = Article(title: "Swift", author: "Apple", publishDate: Date())
let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .iso8601
let jsonData = try? encoder.encode(article)
// Decode from JSON
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
let json = """
{"title":"Swift","author":"Apple","publishDate":"2024-01-01T00:00:00Z"}
"""
if let data = json.data(using: .utf8) {
let decoded = try? decoder.decode(Article.self, from: data)
}
// CUSTOM KEYS FOR CODABLE
struct Product: Codable {
var productName: String
var productPrice: Double
enum CodingKeys: String, CodingKey {
case productName = "name"
case productPrice = "price"
}
}
// IDENTIFIABLE - Unique identity (SwiftUI)
struct Task: Identifiable {
let id = UUID()
var title: String
var isCompleted: Bool
}
// CUSTOMSTRINGCONVERTIBLE - String representation
struct Vector: CustomStringConvertible {
var x: Double
var y: Double
var description: String {
return "(\(x), \(y))"
}
}
print(Vector(x: 3, y: 4)) // (3.0, 4.0)
// CUSTOMDEBUGSTRINGCONVERTIBLE - Debug output
struct Config: CustomDebugStringConvertible {
var settings: [String: Any]
var debugDescription: String {
return "Config(\(settings.count) settings)"
}
}
// SEQUENCE - For-in loop support
struct Countdown: Sequence {
let start: Int
func makeIterator() -> CountdownIterator {
return CountdownIterator(count: start)
}
}
struct CountdownIterator: IteratorProtocol {
var count: Int
mutating func next() -> Int? {
if count <= 0 { return nil }
defer { count -= 1 }
return count
}
}
for i in Countdown(start: 5) {
print(i) // 5, 4, 3, 2, 1
}
// COLLECTION - Array-like access
struct Stack<Element>: Collection {
var items: [Element] = []
var startIndex: Int { 0 }
var endIndex: Int { items.count }
subscript(index: Int) -> Element {
return items[index]
}
func index(after i: Int) -> Int {
return i + 1
}
}
// EXPRESSIBLEBY... - Literal initialization
struct Meters: ExpressibleByIntegerLiteral {
var value: Double
init(integerLiteral value: Int) {
self.value = Double(value)
}
}
let distance: Meters = 100 // Creates Meters(value: 100.0)Try It Yourself!
Create a Movie struct that conforms to Codable, Equatable, Comparable (by rating), and CustomStringConvertible!