JSON to Swift: Codable models
Swift's Codable protocol lets JSONDecoder turn data into typed values, provided you declare matching types. This JSON to Swift generator writes those models from a sample: a struct per object shape with let properties, Swift types chosen from the values, optionals where a key can be missing, and CodingKeys that map Swift names to JSON keys.
Example
{
"id": 42,
"title": "Write docs",
"done": false,
"priority": 2.5,
"tags": ["docs"],
"owner": { "name": "Ada", "email": null },
"due": "2026-10-01T09:00:00Z",
"subtasks": [
{ "title": "Outline", "estimate": 2 },
{ "title": "Draft" }
]
}
// MARK: - Task
struct Task: Codable {
let id: Int
let title: String
let done: Bool
let priority: Double
let tags: [String]
let owner: Owner
let due: Date
let subtasks: [Subtask]
enum CodingKeys: String, CodingKey {
case id = "id"
case title = "title"
// ...
}
}
// MARK: - Subtask
struct Subtask: Codable {
let title: String
let estimate: Int?
}
estimate is Int? because one subtask lacks it; JSONDecoder decodes missing keys as nil for optionals. The ISO string due is a Date.
What full mode adds
- CodingKeys for every type, so Swift property names in camelCase can differ from JSON keys such as
user_name. - Convenience initializers:
try Task(json)from a string,Task(data:)fromData, andTask(fromURL:). - Encoding helpers:
jsonData()andjsonString(). with(...)functions that return a copy with some properties changed, handy with immutableletproperties.newJSONDecoder()andnewJSONEncoder(), configured with ISO 8601 date handling, plus aJSONNulltype for values that are always null.
With Types only, you get plain structs without Codable conformance. Add : Codable yourself, and if JSON keys are snake_case, set decoder.keyDecodingStrategy = .convertFromSnakeCase instead of writing CodingKeys.
Decoding dates correctly
The default JSONDecoder expects dates as seconds since 2001, not ISO strings, which is a common source of "Expected to decode Double but found a string" errors. The generated newJSONDecoder() handles ISO 8601 for you. If you decode manually, set decoder.dateDecodingStrategy = .iso8601; for fractional seconds you need a custom formatter.
When decoding fails
JSONDecoder is strict. If a non-optional property is missing from the data, decoding throws DecodingError.keyNotFound; if a value has the wrong type, it throws typeMismatch with a coding path that shows exactly which property failed. Print the error in a catch block while developing. The usual fix is to make the property optional, because the sample you generated from happened to always contain it while the real API does not. For string fields with a known set of values, you can replace String with an enum, ideally with a fallback case so a new server value does not break decoding.
Type mapping
| JSON | Swift |
|---|---|
| integer / decimal | Int / Double |
| boolean / string | Bool / String |
| ISO date | Date |
| array | [T] |
| optional | T? |
| null only | JSONNull (full) / NSNull (types only) |
Tips
- Change
structtoclassonly if you need reference semantics; structs are the Swift norm for models. - For SwiftUI lists, add
Identifiableconformance when the model has anid. - Replace
JSONNullwith a real optional type such asString?once you know what the field contains.
Working with XML feeds? Try XML to Swift. For Objective-C code bases, JSON to Objective-C generates classes with properties. Validate your sample with the JSON Validator. All processing happens in your browser.