JSON to Swift Codable
Paste a JSON response and get Swift types that decode it with JSONDecoder: one Codable struct (or final class) per object, nested objects as their own types with singular names, properties optional where some examples leave them out or set them to null, and the decoder lines to go with them. Anything the sample cannot settle is marked rather than guessed. Your JSON stays in this page.
Keys that are valid Swift names are used as they are (user_name stays user_name). A key such as "my-key" gets a camelCase name and a CodingKeys entry.
Letters, digits and underscores. Anything else is cleaned up.
Off: those properties become String? with a // TODO comment so you choose the type. On: they get a small JSONValue enum that decodes any JSON value.
- Declarations
- 3
- Input size
- 345 B
The sample does not settle these types
Root.manageris null everywhere in the sample
Each one is marked // TODO in the code. A String? property only decodes text or null, so decoding fails on any other value until you choose the type (or turn on the JSONValue option).
Swift types
import Foundation
struct Root: Codable, Identifiable {
let id: Int
let user_name: String
let is_active: Bool
let score: Double
let created_at: Date
let `default`: String
let manager: String? // TODO: only null in the sample; set the real type
let tags: [String]
let orders: [Order]
let address: Address
}
struct Order: Codable, Identifiable {
let id: String
let total: Double
let coupon: String?
}
struct Address: Codable {
let street: String
let zip: String
}
Decoding
The lines to put where you decode. data is the Data you received, for example from URLSession.
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
let value = try decoder.decode(Root.self, from: data)How it works
- •The JSON is read with a strict parser that keeps each number exactly as written, so 1 becomes Int and 1.0 becomes Double. A field that is 1 in one place and 2.5 in another becomes Double.
- •Every element of an array of objects is merged into one type. A key missing from some elements, or null in any of them, makes the property optional.
- •Nested objects become their own types, named after the key in singular form: items becomes Item, categories becomes Category. Names that would hide a Swift or Foundation type, such as Data or Date, get the parent's name in front (RootData).
- •Keys that are not valid Swift names get a camelCase property and a CodingKeys entry. Swift keywords such as default, class and self are written in backticks.
- •Strings in ISO 8601 date-time form become Date, and the decoding lines set dateDecodingStrategy to .iso8601, or to a small custom strategy when the dates have fractional seconds, which .iso8601 does not read.
- •Identifiable is added when an object has a plain id. Classes are final and get == and hash(into:) written out, because Swift only provides them for structs and enums.
What it does not do
- •It only knows what your sample shows. A field that is sometimes missing in real responses but present in every example here is not optional; paste several examples in an array to cover them.
- •It does not turn string values into enums. If status is always "open" or "closed", you may want an enum with String raw values; that is a choice the sample cannot make for you.
- •Dates other than ISO 8601 text, such as Unix timestamps or "2026-03-07", stay Int, Double or String. Use .secondsSince1970 or a DateFormatter for those.
- •Objects with the same shape under different keys become separate types; merge them by hand if they are the same thing.
- •It does not write SwiftData @Model classes or custom init(from:) code.
- •Property names are ASCII. A key made only of other characters (for example Chinese) gets a name such as field, with a CodingKeys entry for the real key.
Frequently asked questions
- Should I use CodingKeys or keyDecodingStrategy = .convertFromSnakeCase?
- Use .convertFromSnakeCase when the whole API is consistently snake_case: the types stay short and the decoder does the mapping. Use CodingKeys when keys are mixed, contain characters such as hyphens, or when you want the mapping to survive whichever decoder someone uses. Be aware that the strategy turns user_ID into userId and api_URL into apiUrl, so property names must match that exactly.
- Why is a property optional when it has a value in my JSON?
- Because in at least one element of an array it is missing or null. JSONDecoder throws keyNotFound for a missing non-optional key and valueNotFound for null, so the property has to be optional for every element to decode.
- Why is a number Double and not Int?
- Because it was written with a decimal point or exponent somewhere (1.0, 2.5, 1e3), or it is larger than Int can hold. JSONDecoder refuses to decode 2.5 into an Int but happily decodes 2 into a Double, so Double is the type that works for every value seen.
- My dates have milliseconds and .iso8601 fails. Why?
- JSONDecoder's .iso8601 strategy does not accept fractional seconds such as 2026-03-07T17:50:56.123Z. When the sample has them, the decoding lines use a custom strategy with ISO8601DateFormatter and .withFractionalSeconds, and fall back to whole seconds.
- What do the TODO comments mean?
- The sample does not show a usable type there: the value is always null, the array is empty, or the field holds different types (a number in one place, text in another). The property is written as String? so the code compiles, but it only decodes text or null. Choose the real type, or turn on the JSONValue option to get an enum that decodes anything.
- struct or class?
- struct is the usual choice for decoded data: it is a value, it is Sendable when its properties are, and Swift writes Equatable and Hashable for you. Choose final class when you need reference semantics, for example to share one decoded object between views and change it in place.