Codable and JSON

struct Post: Codable, Identifiable {
    let id: Int
    let userId: Int
    var title: String
    var body: String
    var tags: [String]?                       // optional: missing keys are fine
    let publishedAt: Date

    enum CodingKeys: String, CodingKey {      // rename keys
        case id, userId = "user_id", title, body, tags
        case publishedAt = "published_at"
    }
}

let json = """
{ "id": 1, "user_id": 7, "title": "Hello", "body": "World", "published_at": "2025-10-24T10:00:00Z" }
""".data(using: .utf8)!

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
// decoder.keyDecodingStrategy = .convertFromSnakeCase   // alternative to CodingKeys
let post = try decoder.decode(Post.self, from: json)

let encoder = JSONEncoder()
encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
encoder.dateEncodingStrategy = .iso8601
let data = try encoder.encode(post)
print(String(decoding: data, as: UTF8.self))

39.1 Custom Encoding/Decoding

Nested containers, singleValueContainer, unkeyedContainer, PropertyListEncoder/Decoder, error handling with DecodingError (.keyNotFound, .typeMismatch, .dataCorrupted), and dynamic JSON (JSONSerialization, or a custom JSONValue enum).

struct Temperature: Codable {
    let celsius: Double

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        if let c = try container.decodeIfPresent(Double.self, forKey: .celsius) {
            celsius = c
        } else {
            let f = try container.decode(Double.self, forKey: .fahrenheit)
            celsius = (f - 32) * 5 / 9
        }
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(celsius, forKey: .celsius)
    }

    enum CodingKeys: String, CodingKey { case celsius, fahrenheit }
}

enum Shape: Codable {                          // enums with associated values are Codable (Swift 5.5+)
    case circle(radius: Double)
    case square(side: Double)
}