PDFFlare
8 min read

How to Convert JSON to Swift Codable Struct (Step-by-Step)

Looking for a JSON to Swift Codable generator? Here's the no-friction approach. Your iOS app fetches a JSON payload with URLSession. Now you need a Codable struct so JSONDecoder can hand you a typed value instead of a dictionary. The API uses snake_case, your Swift app uses camelCase — bridging the two with a hand-typed CodingKeys enum is rote work.

In this guide you'll learn how to convert JSON to a Swift Codable struct using PDFFlare's JSON to Swift tool — how Codable + JSONDecoder map JSON keys onto Swift properties, when CodingKeys is required, how to make optional fields actually optional, and the SwiftUI preview pattern that makes Codable types pay back tenfold.

Why Codable?

Codable (a typealias for Encodable & Decodable) is built into Foundation and is the only Apple-supported way to serialize Swift values. JSONDecoder().decode(Type.self, from: data) handles the JSON-to-Swift mapping with zero runtime dependencies. Pair it with URLSession or async/await URLSession.shared.data(for:) and you have a typed API client in three lines.

How to Convert JSON to a Swift Codable Struct (Step by Step)

  1. Open PDFFlare's JSON to Swift tool.
  2. Paste a real production JSON sample. Snake_case keys are fine — the tool will emit a CodingKeys enum to map them onto camelCase Swift properties.
  3. Set the root struct namevia the "Root struct" field. UserResponse, WeatherForecast, StripePaymentIntent — pick the domain noun.
  4. Click Convert to Swift. Each unique object shape becomes its own struct conforming to Codable. CodingKeys appear only when at least one JSON key needs remapping; clean output for camelCase APIs.
  5. Drop into your Xcode project. let response = try JSONDecoder().decode(UserResponse.self, from: data) — typed values, compile-time safe.

Real Use Cases for JSON to Swift

iOS API clients with URLSession

The async/await variant of URLSession returns (Data, URLResponse). Pipe the data into JSONDecoder().decode(Type.self, from:)and you're done.

SwiftUI #Preview blocks

Paste a real API response, generate the struct, instantiate sample values for previews. Type-safe previews from real data — no more mocked dictionaries.

Server-side Swift (Vapor, Hummingbird)

Same Codable pattern works for incoming request bodies. The Vapor Content protocol is built on Codable, so generated structs drop in without modification.

Cross-platform packages

SwiftPM packages targeting both iOS and Linux server code share Codable types. Generate once, reuse everywhere.

Common Mistakes (and How to Avoid Them)

  • Forgetting JSONDecoder's keyDecodingStrategy. If you removed the CodingKeys enum, set decoder.keyDecodingStrategy = .convertFromSnakeCase globally so the keys still map.
  • Not marking optional fields. Decode throws .keyNotFound if a non-optional field is missing in the real response. Mark fields the API may omit as String?.
  • Using Int for big IDs. 64-bit numeric IDs from the API may overflow Int on 32-bit targets. Refine to Int64 manually.
  • Missing the date strategy. Date strings stay as strings unless you set decoder.dateDecodingStrategy — pick .iso8601 for RFC 3339 strings or use a custom formatter.

Privacy: Your JSON Stays Local

Conversion runs in your browser. The JSON never uploads — safe for tokenized API responses and internal payloads.

Related Workflows in the JSON Suite

Adjacent tools you might find useful while working on the same JSON document: the JSON to Kotlin and JSON to TypeScript both pair well with the conversion above. The first handles a different output format that consumers of your data may prefer; the second covers the validation side of the same workflow.

Related Tools

Workflow Tips for iOS Teams

Working with generated Codable structs day to day, several workflow habits compound into real productivity gains for an iOS or macOS team. The first is to keep generated structs in a dedicated module or folder, clearly separated from your domain model. The generator produces a faithful mirror of the JSON wire format. That format almost never matches the model your app logic actually wants — you usually want richer types, computed properties, and methods that no JSON document carries. Treat the generated structs as a transport layer that you map into domain types in a service layer.

A related habit is to write a small extension or initializer on your domain model that constructs from the transport struct. When the API changes a field, you update the generator output and the mapping — your domain logic remains untouched. This separation of concerns sounds like ceremony for small payloads but pays for itself the first time an upstream team renames a field without telling you. The mismatch surfaces at the boundary, the fix is mechanical, and your view code never noticed anything happened.

For SwiftUI specifically, prefer value types end to end. Codable structs are already value types, so this is the default. But resist the temptation to bridge to a class hierarchy for “ergonomics” — SwiftUI's data flow is designed around value semantics, and reference types fight the framework. Keep your transport structs and domain types as structs, use enums for variants, and let the framework do its job.

Cross-team contracts deserve careful handling. If your iOS app consumes a backend owned by another team, export your generated transport structs as a versioned contract — a small Swift Package that both sides depend on, or a snapshot file checked into a shared repo with both teams committing together. Without this discipline, drift creeps in and integration bugs become detective work. With it, both teams share a single compiler-enforced source of truth, and changes require explicit acknowledgment from both sides.

Production Patterns for JSON to Swift Codable

A generated Codable struct is a starting point. These tweaks turn it into the kind of code you ship in a real iOS app:

Optionals and Default Values

The generator can't infer optionality from a single sample. If a field might be absent, mark it with ?. For fields that should have a sensible fallback, prefer a default via a custom init(from:)rather than making the field optional — readers shouldn't have to unwrap a value that always has a meaningful value.

Date Decoding Strategy

ISO-8601 strings work out of the box if you set JSONDecoder.dateDecodingStrategy = .iso8601 before decoding. For Unix timestamps, set .secondsSince1970. For weird custom formats, .formatted(formatter) with a DateFormatter tuned to the API. Set this once on your global decoder, not per call site.

Snake_case Key Mapping

When every API you talk to uses snake_case keys (Rails apps, Python services), set JSONDecoder.keyDecodingStrategy = .convertFromSnakeCase and drop most of the CodingKeys enums entirely. Keep CodingKeys only for the truly weird mappings.

When to Reach for a Different Approach

A few alternatives worth knowing about:

  • OpenAPI codegen — when the upstream service publishes a spec, generate the full model layer and a client in one shot.
  • Building Android too? JSON to Kotlin emits data classes for kotlinx.serialization — same shape, different idioms.
  • For schema validation, generate a JSON Schema from your sample first.

Common Mistakes to Avoid

  1. Generating from a single sample. Capture two or three diverse payloads and merge by hand. Fields appearing in some but not others are your optional candidates.
  2. Not testing decode failures. Build a unit test that decodes a real production capture. If decoding throws, you catch the bug locally, not in TestFlight reports.
  3. Mixing Decodable-only and full Codable.If you only ever decode (consume an API), you don't need Encodable conformance. Drop it for slightly faster compile times. Keep both only when you actually serialize back.
  4. Treating UUID strings as String. If the API returns UUIDs, type the field as UUID directly — Swift Codable handles the conversion, and you get type safety on the consumer side.
  5. Skipping nested struct extraction.If a JSON object has a deeply nested structure used in only one place, the generator emits separate structs for it. That's correct. Don't flatten everything into the root struct for “simplicity” — it makes the type names ambiguous later.

Real-World Use Cases

  • iOS app consuming a REST API. Generate Codable structs from the actual response, drop into your networking layer, and the parse layer is type-safe end to end. SwiftUI views bind directly to the struct.
  • macOS menu bar app reading a webhook. Capture a payload, generate the struct, and write the handler in pure Swift — no JSONSerialization, no[String: Any] casting.
  • Sharing models across an iOS + watchOS app. Codable structs work everywhere Swift runs. Generate once, share via a Swift Package, both targets parse the same shape.
  • Migrating off SwiftyJSON. Replace fragile stringly-typed access with a real Codable struct — compile-time safety and a 2-3× perf improvement come along for free.

Polishing the Generator's Output

Codable in Swift hides a remarkable amount of complexity behind a small public surface. Whatever your specific use case, treat the generated output as a draft that deserves a careful read-through. Generators are excellent at producing the mechanical structure of an artifact and not at the editorial decisions that make the difference between something a colleague will tolerate and something a colleague will appreciate. Read every section of the output the way you would read a piece of writing you were proofreading for a friend. Look for inconsistent naming, missed opportunities to consolidate similar items, and places where the structure is mechanically correct but conceptually awkward. The five minutes spent on this review are the difference between an artifact that pays back over months and one that needs a second pass before it can be used. The generator handles the heavy lifting; you handle the polish that turns a draft into a deliverable. This division of labor is what makes generated code worthwhile in the first place. Without that final pass of human editorial judgment, the generator's output is merely fast rather than valuable, and the value matters more than the speed in nearly every real production setting.

Wrapping Up

Codable + a generated struct is the lowest-friction way to take a JSON API and make it feel native in Swift. Use PDFFlare's JSON to Swift tool to skip the boilerplate, and refine optionals, dates, and big-int fields where the inference can't guess.