Swift Codable
Encode and decode Swift types using Codable (Encodable & Decodable) with
JSONEncoder, JSONDecoder, and related APIs. Targets Swift 6.3 / iOS 26+.
Contents
- Decode and Verify Workflow
- Basic Conformance
- Custom CodingKeys
- Custom Decoding and Encoding
- Nested and Flattened Containers
- Heterogeneous Arrays
- Date Decoding Strategies
- Data and Key Strategies
- Lossy Array Decoding
- Single Value Containers
- Default Values for Missing Keys
- Encoder and Decoder Configuration
- Codable with URLSession
- Codable with SwiftData
- Codable with UserDefaults
- Common Mistakes
- Review Checklist
- References
Decode and Verify Workflow
- Decode representative success, missing, null, malformed, acronym-key, and date fixtures.
- On failure, inspect
DecodingError, itscodingPath, and the raw payload. - Correct only the mismatched model, key, container, or strategy; do not hide contract failures with lossy decoding.
- Rerun fixtures and encode/decode round trips where both directions are part of the contract.
Basic Conformance
When all stored properties are themselves Codable, the compiler synthesizes
conformance automatically:
Prefer Decodable for read-only API responses and Encodable for write-only.
Use Codable only when both directions are required.
Custom CodingKeys
Rename JSON keys without writing a custom decoder by declaring a CodingKeys
enum:
Every stored property must appear in the enum. Omitting a property from
CodingKeys excludes it from encoding/decoding -- provide a default value or
compute it separately.
Custom Decoding and Encoding
Override init(from:) and encode(to:) for transformations the synthesized
conformance cannot handle:
Nested and Flattened Containers
Use nestedContainer(keyedBy:forKey:) to navigate and flatten nested JSON:
Chain multiple nestedContainer calls to flatten deeply nested structures.
Also use nestedUnkeyedContainer(forKey:) for nested arrays.
Heterogeneous Arrays
Load Advanced Codable Patterns [blocked] for discriminator-based mixed arrays.
Date Decoding Strategies
Configure JSONDecoder.dateDecodingStrategy to match your API:
Set the matching strategy on JSONEncoder:
encoder.dateEncodingStrategy = .iso8601
Data and Key Strategies
Use key strategies only for mechanical snake_case-to-camelCase mappings.
convertFromSnakeCase maps by spelling, not Swift acronym/initialism policy:
image_url, base_uri, and user_id match imageUrl, baseUri, and
userId only. If the Swift model uses imageURL, baseURI, or userID,
declare explicit CodingKeys; the strategy will not synthesize those names.
Lossy Array Decoding
Use lossy arrays only when partial success is part of the product contract; load Lossy Arrays [blocked].
Single Value Containers
Use singleValueContainer() for type-safe primitive wrappers; see
Single-Value Wrappers [blocked].
Default Values for Missing Keys
Stored defaults do not make synthesized decoding tolerate missing nonoptional keys. Load Missing-Key Defaults [blocked] when the contract assigns explicit fallback behavior to missing or null values.
Encoder and Decoder Configuration
Keep matching strategies at the transport/file-format boundary. Load Encoder Configuration [blocked] for nonconforming floats and property-list guidance.
Codable with URLSession
Codable with SwiftData
Keep schema values typed and route persistence design to swiftdata; see
Persistence Boundaries [blocked].
Codable with UserDefaults
Use primitives for small preferences. Load
Persistence Boundaries [blocked]
for a small Codable RawRepresentable/@AppStorage handoff; use a real
persistence layer for larger or durable data.
Common Mistakes
1. Not handling missing defaulted fields:
2. Failing entire array when one element is invalid:
3. Date strategy mismatch:
4. Force-unwrapping decoded optionals:
5. Using Codable when only Decodable is needed:
6. Manual CodingKeys for simple snake_case APIs:
Review Checklist
- Types conform to
Decodableonly when encoding is not needed -
decodeIfPresentused with defaults for optional or missing keys -
keyDecodingStrategy = .convertFromSnakeCaseused for simple snake_case APIs, with CodingKeys retained for acronym spellings -
dateDecodingStrategymatches the API date format - Arrays of unreliable data use lossy decoding to skip invalid elements
- Custom
init(from:)validates and transforms data instead of post-decode fixups -
JSONEncoder.outputFormattingincludes.sortedKeysfor deterministic test output - Wrapper types (UserID, etc.) use
singleValueContainerfor clean JSON - Generic
APIResponse<T>wrapper used for consistent API envelope handling - No force-unwrapping of decoded values
- Persistence boundary is explicit: SwiftData only for compatible noncomputed model properties,
@AppStorage/UserDefaults only for small primitive orRawRepresentablepreferences
References
- Advanced Codable patterns [blocked] -- mixed arrays, lossy decoding, wrappers, defaults, configuration, and persistence boundaries
- Codable -- protocol combining Encodable and Decodable
- JSONDecoder -- decodes JSON data into Codable types
- JSONEncoder -- encodes Codable types as JSON data
- CodingKey -- protocol for encoding/decoding keys
- JSONDecoder.KeyDecodingStrategy.convertFromSnakeCase -- snake-case conversion behavior and limitations
- Encoding and Decoding Custom Types -- Apple guide on custom Codable conformance
- Using JSON with Custom Types -- Apple sample code for JSON patterns
- Preserving your app's model data across launches -- SwiftData model property compatibility
