JSON to Swift Codable
Generate Swift Codable structs or classes from sample JSON, with CodingKeys for wire names — entirely on this device.
final applies to classes only — a struct has nothing to seal.
Sendable is dropped from a class with var properties.
The generated Codable types appear here as you type.
Press ? for shortcuts
Do it offline:$ npx quicktype sample.json --lang swift --top-level Root --coding-keys --no-initializers --no-maps --no-enums --no-date-times --no-uuids --no-integer-strings --no-boolean-strings --acronym-style pascal -o Models.swift
How to use it
- Paste one response, or drop a .json file — the Swift pane refreshes as you type.
- Open a tab per response variant: anything one variant omits comes back optional.
- Choose the Swift side: type name, struct or class, access level, CodingKeys policy, protocols, Sendable, let or var, Int or Double.
- Copy the source, save it as Models.swift, or share a permalink carrying every tab and setting.
Paste one response or several and read Swift back: a Codable struct per object shape, a CodingKeys enum holding the wire spelling of every key, and a question mark on anything a sample left out. Keys arrive as Swift names — user_id becomes userID — while the JSON side of the mapping stays byte-exact. Nothing is uploaded; the samples stay in this tab.
Examples
{"user_id":42,"avatar-url":"https://cdn.example/a.png","display_name":"Ada"}import Foundation
// MARK: - Root
struct Root: Codable {
let userID: Int
let avatarURL: String
let displayName: String
enum CodingKeys: String, CodingKey {
case userID = "user_id"
case avatarURL = "avatar-url"
case displayName = "display_name"
}
}
The enum underneath holds each original spelling character for character. avatar-url could never have been a property name; user_id could, but the convention wants userID, acronym capitalised. Decoding is unaffected either way, since JSONDecoder reads the case's raw value and never the property name.
{"id":1,"email":"ada@example.com","nickname":"ada"}
{"id":2,"email":"cy@example.com"}import Foundation
// MARK: - Root
struct Root: Codable {
let id: Int
let email: String
let nickname: String?
enum CodingKeys: String, CodingKey {
case id = "id"
case email = "email"
case nickname = "nickname"
}
}
The second reading drops nickname, so nickname alone gains a question mark; id and email occur in both tabs and stay non-optional. From a single sample every key looks mandatory, and the first response that omits one throws keyNotFound instead of handing you nil.
{"archived_at":null,"tags":[]}import Foundation
// MARK: - Root
struct Root: Codable {
let archivedAt: JSONNull?
let tags: [JSONAny]
enum CodingKeys: String, CodingKey {
case archivedAt = "archived_at"
case tags = "tags"
}
}
A key that is null in every tab and an array that never holds an element leave nothing to infer from. The generator defines both placeholders itself, in a 245-line block switched off here to keep the listing readable — and a chip warns that the file will not build without it.
Every example on this page is executed against the same engine that powers the tool, on every build.
Gotchas worth knowing
The enum, not the property name, is the wire contract
Rename a property however Swift prefers and the payload it decodes does not move, because the case keeps the original key as its raw value. Emitting the enum everywhere is the default, so the mapping stays visible in review.
A question mark is evidence, never a guess
A property is made optional only because one open tab lacked that key. Paste the empty state, an error body and a full record side by side, and optionality settles where the API really puts it.
JSONNull? and [JSONAny] mean the samples stayed silent
Both are types the generator writes into the same file, in a block teaching JSONAny to decode arbitrary values. Leave it on and the unit is self-contained; richer samples retire the placeholders and the block together.
One token decides Int against Double
JSON has a single number type. A lone reading of 1.0 is whole, so the property is typed Int, while 9223372036854775807 outruns JavaScript-based inference and arrives as Double, unable to carry its last digits. Take that one as a String.
A mixed array becomes a union enum
An array seen holding a number, a string and a boolean does not fall back to the untyped placeholder. The generator writes an enum with one case per observed shape and an init(from:) trying each in turn.
Not compiled here
swiftc does not run in the environment that builds this page. Every listing above is replayed through the generator and byte-compared against frozen vectors, which pins bytes rather than builds. Let Xcode have the final word.
Common errors
Sample 2: Unexpected "'" at line 1, column 2 — probable cause: single-quoted strings are JSON5, not JSON; use double quotes.- Every open tab must parse first. The tab number appears only while more than one is open, and the caret frame points at the byte that stopped the parser.
no Swift type could be generated — this JSON holds no object anywhere, so there are no properties to model; paste an object like {"id":1} or an array of objects- Codable models properties, and a bare list of numbers or a lone quoted string has none. Wrap the value in an object, or paste an array of objects — that shape generates, from the element up.
Input is larger than the 10 MB limit for this tool.- The tabs are weighed together before anything runs, and past the ceiling the run is refused rather than quietly truncated. Aim the command below at an export that large.
`snowflake` (/snowflake): 9223372036854775807 is beyond ±2^53 — the property is typed Double, which cannot hold every digit; decode it as String or Int64 by hand if the exact value matters- An advisory, not a failure: the model is generated. The digits were lost before any Swift type was chosen, because the sample crossed a JavaScript number on the way to the renderer.
`ratio` (/ratio): 1.0 is written as a float but its value is whole — the property is typed Int; add a sample with a fractional value to get Double- The mirror image of the previous line. A rate reading 1.0 in the tab you pasted looks integral, so add a fractional reading — or widen every integer — before a 0.5 arrives and the decode throws.
FAQ
Should the generated model be a struct or a class?
Struct is the default and the calmer decode target, a value type nothing else can mutate behind your back. Reach for class when reference semantics matter, and expect two chips: final has nothing to seal on a struct, and Sendable is dropped from a var class.
Can I stop CodingKeys from repeating names that already match?
Set the CodingKeys control to the renamed-only mode. An enum whose rows all repeat their property names goes whole — Swift derives those itself — and a chip counts it. One renamed key keeps the enum complete: the decoder reads only declared cases.
Will the generated types conform to Hashable?
Ask in the protocols control and the declaration gains the conformance, but the generator keeps its caveat at the top of the file: the compiler synthesises neither Hashable nor Equatable for a type using JSONAny, nor Hashable where collections appear.
Keyboard shortcuts
- ⌘/Ctrl + Enter
- Run the generator — required once the tabs pass 2 MB.
- ⌘/Ctrl + S
- Write the Swift unit to a .swift file, not the page.
- ⌘/Ctrl + C
- Copies the output pane when nothing is selected.
- ⌘/Ctrl + Shift + C
- Copy the output despite an active selection.
- t
- Switch theme, unless a text field has focus.
- ? or ⌘/Ctrl + /
- Show the shortcut overlay.
- Esc
- Close the topmost dialog.