What does keyDecodingStrategy do, and when does convertFromSnakeCase not match?

2.5.1 · L1 · Interview

Predict first

What does this print?

struct User: Decodable { let userID: Int }
let d = JSONDecoder()
d.keyDecodingStrategy = .convertFromSnakeCase
print((try? d.decode(User.self, from: Data(#"{"user_id": 1}"#.utf8))) as Any)
  • Optional(User(userID: 1))
  • nil

Mental model. The strategy is an automatic translator for key names. It translates one word at a time and does not know your acronyms.

By default a Swift property name must equal the JSON key. keyDecodingStrategy = .convertFromSnakeCase converts each incoming key (user_id) to lower camel case (userId) before matching, so you do not write a CodingKeys enum for every type. The encoder’s .convertToSnakeCase does the reverse.

The conversion capitalises only the first letter after each underscore. So user_id becomes userId, and a property named userID will not match: decoding fails with a missing key. Acronyms are the usual culprit. Rename the property, add a CodingKeys case, or use .custom for a mixed bag of conventions.

user_id becomes userId, which is not userID, so the key is missing and decoding throws.

Common traps

  • Naming a property userID or url vs URL. The converted key is userId; anything else misses.
  • Expecting the strategy to run for keys you list in CodingKeys. Explicit keys are used as written, so mixing both needs care.

They might follow up with

  • When is a CodingKeys enum still better? When names differ by more than case, or only a few keys are odd.
  • Does the strategy cost anything? A small string transformation per key, which rarely matters next to parsing.

Related questions

  • 2.5.4 How do you read a DecodingError, and how do you decode tolerantly?
  • 1.10.1 How does Codable synthesis work? When do you write CodingKeys or a custom init(from:)?

How are dates encoded and decoded in JSON, and why are they 31 years… →