What does keyDecodingStrategy do, and when does convertFromSnakeCase not match?
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
userIDorurlvsURL. The converted key isuserId; 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
CodingKeysenum 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… →