Skip to content

JSON to Swift Struct (Codable)

Convert JSON to Swift structs that conform to Codable, ready for JSONDecoder in an iOS or macOS app.

Input · JSON
Output · Swift
Valid JSONRuns in your browser

Load from a URL

Your browser fetches it directly, so the server must allow cross-origin requests (CORS).

A post, from JSON to Swift structs

With Post as the Root name, the sample becomes:

{
  "postId": 42017,
  "author": { "handle": "@lena", "verified": true },
  "text": "First snow in Tromsø",
  "media": [
    {
      "type": "photo",
      "url": "https://cdn.example.com/p/42017-1.jpg",
      "altText": "Snowy street at dusk"
    }
  ],
  "likes": 318,
  "replies": 12,
  "postedAt": "2026-11-04T07:41:00Z",
  "location": null
}
import Foundation

struct Post: Codable {
    let postId: Int
    let author: Author
    let text: String
    let media: [Media]
    let likes: Int
    let replies: Int
    let postedAt: String
    let location: JSONValue?
}

struct Media: Codable {
    let type: String
    let url: String
    let altText: String
}

struct Author: Codable {
    let handle: String
    let verified: Bool
}

Properties are let; make them var to edit models after decoding. Whole numbers are Int, decimals Double. location held only null, so it falls back to JSONValue?, a small enum appended to the output whenever a value could be any JSON type. It stores every number as a Double, so swap in a real type once you know it. Each ? marks a value that was null or missing somewhere, after array items are merged (rules on JSON to TypeScript). Explore large payloads first in the JSON viewer.

Decoding with JSONDecoder

import Foundation

func loadPost(from body: String) throws -> Post {
    try JSONDecoder().decode(Post.self, from: Data(body.utf8))
}

With URLSession, pass the data from try await URLSession.shared.data(from: url) to the same call. Optionals are decoded with decodeIfPresent, so a missing key and null both give nil, and undeclared keys are ignored. To get a Date for postedAt, change its type and set dateDecodingStrategy to .iso8601. The structs are Codable, so JSONEncoder().encode(post) writes JSON with the original keys.

When CodingKeys appear

A CodingKeys enum is only written when a property name differs from its JSON key, as with these snake_case keys and the root name Track:

{ "track_id": 812, "title": "Northern Lights", "duration_ms": 214000, "explicit": false }
import Foundation

struct Track: Codable {
    let trackId: Int
    let title: String
    let durationMs: Int
    let explicit: Bool

    enum CodingKeys: String, CodingKey {
        case trackId = "track_id"
        case title
        case durationMs = "duration_ms"
        case explicit
    }
}

title and explicit are listed too: once a type declares CodingKeys, every stored property without a default must be in it. A Swift keyword such as default keeps its name in backticks and needs no mapping. Don’t add keyDecodingStrategy = .convertFromSnakeCase on top: it turns track_id into trackId before matching, and then no key named track_id exists. For Android, JSON to Kotlin writes matching data classes.

Questions

Why structs and not classes?

Structs are value types and come with a memberwise initializer, which suits data models. Codable works on classes too, but a class does not get that initializer for free.

How do I decode an array of posts?

Ask the decoder for an array type: try JSONDecoder().decode([Post].self, from: data).

Does it work with Alamofire?

Yes. Any Decodable type works with Alamofire, for example through responseDecodable(of: Post.self).

Related tools

Esc