100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Swift

Codable and JSON Parsing

Learn how Swift's Codable protocol pair enables automatic, type-safe encoding and decoding between Swift types and formats like JSON.

Data & NetworkingBeginner8 min readJul 8, 2026
Analogies

Codable and JSON Parsing

Codable is a type alias for the combination of Encodable and Decodable, two protocols that let Swift automatically synthesize code to convert your types to and from external representations like JSON. When every stored property of a struct or class conforms to Codable itself (String, Int, Date, arrays, and nested Codable types all qualify), the Swift compiler generates the encoding and decoding logic for you at compile time — no reflection, no manual key-by-key parsing. This is the standard way iOS apps parse API responses, and it pairs naturally with JSONDecoder/JSONEncoder for JSON specifically, though the same protocols also work with property list and other encoders.

🏏

Cricket analogy: Just as a scorecard app auto-fills runs, wickets, and overs from a standard scoresheet format without a scorer re-typing each field, Codable auto-generates conversion code so a struct's stored properties map straight to JSON without manual parsing.

Automatic Synthesis

The simplest case requires no code at all beyond declaring conformance. If your struct's property names match the JSON keys exactly, Codable synthesis handles everything: JSONDecoder().decode(User.self, from: data) will populate a User value directly from matching JSON fields. This works because the compiler generates a synthesized CodingKeys enum whose cases mirror your stored properties, and the synthesized init(from:)/encode(to:) implementations use it internally.

🏏

Cricket analogy: Like a scorecard where the JSON field 'runs' maps directly to a struct property named 'runs' with no translation needed, JSONDecoder populates a User type straight from matching JSON keys without any custom mapping code.

swift
struct User: Codable {
    let id: Int
    let name: String
    let email: String
    let signupDate: Date
}

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601

let user = try decoder.decode(User.self, from: jsonData)

Custom Keys with CodingKeys

Real-world APIs frequently use snake_case keys while Swift convention is camelCase. Rather than writing custom init(from:)/encode(to:) by hand, you can either set decoder.keyDecodingStrategy = .convertFromSnakeCase, or define an explicit CodingKeys enum that maps each Swift property to its JSON key name. CodingKeys must conform to String, CodingKey, and any property you omit from it will not be encoded or decoded — which is also a convenient way to exclude a property from serialization entirely.

🏏

Cricket analogy: Like a scorecard feed that uses 'strike_rate' from an Indian broadcast API while your app's model uses strikeRate, you define a CodingKeys enum mapping strikeRate to 'strike_rate' instead of writing a full custom decoder by hand.

swift
struct Article: Codable {
    let id: Int
    let title: String
    let publishedAt: Date

    enum CodingKeys: String, CodingKey {
        case id
        case title
        case publishedAt = "published_at"
    }
}

Custom Decoding Logic

When the JSON shape doesn't map cleanly onto your model — nested objects you want flattened, fields that may arrive as either a string or a number, or values needing validation — you implement init(from decoder: Decoder) throws manually. You obtain a KeyedDecodingContainer via decoder.container(keyedBy: CodingKeys.self), then decode each field explicitly, optionally using decodeIfPresent for optional fields or catching per-field errors to supply defaults.

🏏

Cricket analogy: Like a scorecard where 'overs' might arrive as either '19.4' or 19.4 depending on the feed, you write a manual init(from decoder:) using KeyedDecodingContainer to decode it with fallback logic rather than trusting automatic synthesis.

swift
struct Product: Decodable {
    let id: Int
    let priceCents: Int

    enum CodingKeys: String, CodingKey {
        case id, price
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decode(Int.self, forKey: .id)
        let priceString = try container.decode(String.self, forKey: .price)
        guard let dollars = Double(priceString) else {
            throw DecodingError.dataCorruptedError(
                forKey: .price, in: container,
                debugDescription: "Price is not a valid number string")
        }
        priceCents = Int(dollars * 100)
    }
}

Codable is not limited to JSON. The same struct can be encoded to a property list with PropertyListEncoder, making it easy to persist small model objects to disk or UserDefaults without writing format-specific code.

A single malformed field anywhere in a Codable struct's synthesized decoding will cause the entire decode to throw, even if the rest of the JSON is valid. For APIs with unreliable fields, prefer explicit decodeIfPresent with sensible fallbacks over letting synthesis fail the whole object.

  • Codable = Encodable & Decodable; conformance can be automatically synthesized when all properties are themselves Codable.
  • JSONDecoder/JSONEncoder bridge Codable types to and from JSON Data.
  • CodingKeys maps Swift property names to different JSON key names, or excludes properties from serialization.
  • keyDecodingStrategy = .convertFromSnakeCase handles snake_case-to-camelCase conversion without custom keys.
  • Manual init(from:) implementations handle irregular JSON shapes, type coercion, or validation logic.
  • A single decoding failure anywhere in a type throws for the whole object unless handled with decodeIfPresent or try?.

Practice what you learned

Was this page helpful?

Topics covered

#Swift#IOSWithSwiftUIStudyNotes#MobileDevelopment#CodableAndJSONParsing#Codable#JSON#Parsing#Automatic#StudyNotes#SkillVeris