How does Codable synthesis work? When do you write CodingKeys or a custom init(from:)?

1.10.1 · L2 · Interview

Predict first

What happens when you decode {"name": "Asha"}?

import Foundation

struct User: Decodable {
    let name: String
    let age: Int
}

let json = Data(#"{"name": "Asha"}"#.utf8)
let u = try? JSONDecoder().decode(User.self, from: json)
print(u as Any)
  • age becomes 0
  • Decoding throws, u is nil
  • Crash

Mental model. The compiler reads your struct and writes the translation both ways. CodingKeys is the glossary that renames terms.

If every stored property is Codable, the compiler synthesizes Encodable and Decodable using property names as keys. A missing key is allowed for an optional property (it decodes as nil); a missing key for a non-optional property throws keyNotFound.

Write CodingKeys to rename, skip, or map a subset of properties. Write a custom init(from:) when you need migrations, nested flattening, polymorphic decoding, or defaults for missing keys.

Synthesis ignores extra JSON keys, and it does not validate values — a string where you expected an int throws a typeMismatch.

Synthesized decoding requires every non-optional key. Make age optional, give it a default in a custom init(from:), or decode it with decodeIfPresent.

Common traps

  • “Codable handles schema evolution.” It does not. Adding a non-optional property breaks decoding of old payloads; use an optional or a custom initializer with a default.
  • “CodingKeys is only for renaming.” Omitting a case removes that property from the encoding — useful, but easy to do accidentally.
  • Relying on key order or exact JSON shape. JSONDecoder does not care about order, but it does care about types and missing keys.

They might follow up with

  • What is the difference between decode and decodeIfPresent? The first throws when the key is missing; the second returns nil.
  • How do you decode a heterogeneous array? Try each type in turn, or use a custom container with a discriminator key.

Related questions

  • 1.10.2 What does Sendable mean for a Swift type you write?
  • 1.10.6 You need to add a field to a public struct in a binary SPM library used by five apps. What do you consider?

What does Sendable mean for a Swift type you write? →