Lesson 15 / 25

Codable, Equatable, Hashable and Comparable

Use standard protocols for JSON coding, equality, hashing and ordering.

The protocols you use every day

Codable (shorthand for Encodable & Decodable) converts types to and from external formats. With JSONDecoder and JSONEncoder you decode API responses into structs and encode them back; customise key names with a nested CodingKeys enum or a keyDecodingStrategy such as .convertFromSnakeCase, dates with dateDecodingStrategy (for example .iso8601), and handle unusual shapes with a custom init(from:). Equatable provides ==, Hashable allows use as dictionary keys and in sets, and Comparable enables < and sorting; all are synthesised for many structs and enums. Identifiable (an id property) is required by SwiftUI lists. CustomStringConvertible controls description for printing. Sendable marks types safe to share across concurrency domains (covered later). Combining these standard protocols with value types gives you model objects that are easy to compare, store, serialise and display with almost no boilerplate.

Decoding a JSON API response

Snake-case keys, ISO 8601 dates and a nested array.

import Foundation

struct OrderResponse: Codable, Identifiable, Hashable {
    let id: String
    let customerName: String
    let status: Status
    let placedAt: Date
    let items: [Item]

    enum Status: String, Codable { case placed, paid, shipped, cancelled }

    struct Item: Codable, Hashable {
        let sku: String
        let quantity: Int
        let unitPricePaise: Int
    }
}

let json = """
{"id":"o-1001","customer_name":"Asha","status":"paid","placed_at":"2026-10-03T06:45:00Z",
 "items":[{"sku":"PEN-BLUE","quantity":2,"unit_price_paise":4950}]}
""".data(using: .utf8)!

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
decoder.dateDecodingStrategy = .iso8601

do {
    let order = try decoder.decode(OrderResponse.self, from: json)
    let total = order.items.reduce(0) { $0 + $1.quantity * $1.unitPricePaise }   // 9900 paise
    print(order.customerName, order.status, total)
} catch {
    print("Could not decode order: \(error)")
}

Decoding errors are informative

When decoding fails, print the error: DecodingError tells you the exact key path and problem, such as a missing key or a type mismatch. It is the fastest way to debug API changes.

Quick check: Which protocol lets a Swift type be converted to and from JSON with JSONEncoder and JSONDecoder?

  • Hashable
  • Identifiable
  • Codable
  • Comparable
Answer

Codable — Codable combines Encodable and Decodable for serialisation.