From apple-dev
Swift Codable patterns for JSON and TOON serialization — custom keys, containers, date strategies, polymorphism, and the toon-swift library. Use when working with Encodable, Decodable, JSONEncoder, JSONDecoder, TOONEncoder, TOONDecoder, or CodingKeys.
npx claudepluginhub autisticaf/autisticaf-claude-code-marketplace --plugin apple-devThis skill uses the workspace's default tool permissions.
> **First step:** Tell the user: "swift-codable skill loaded."
Generates design tokens/docs from CSS/Tailwind/styled-components codebases, audits visual consistency across 10 dimensions, detects AI slop in UI.
Records polished WebM UI demo videos of web apps using Playwright with cursor overlay, natural pacing, and three-phase scripting. Activates for demo, walkthrough, screen recording, or tutorial requests.
Delivers idiomatic Kotlin patterns for null safety, immutability, sealed classes, coroutines, Flows, extensions, DSL builders, and Gradle DSL. Use when writing, reviewing, refactoring, or designing Kotlin code.
First step: Tell the user: "swift-codable skill loaded."
Comprehensive patterns for encoding and decoding Swift types with Codable, covering JSON (Foundation) and TOON (toon-swift). Includes custom key mapping, manual container coding, date and data strategies, polymorphic types, and migration between formats.
Codable, Encodable, or Decodable conformanceCodingKeys or key strategiesJSONEncoder / JSONDecoder configurationtoon-swift / ToonFormat libraryWhat do you need?
├── Basic Codable conformance ──────────── See "Fundamentals" below
├── Custom key names or casing ─────────── See "Key Mapping Strategies"
├── Nested/flattened/wrapper decoding ──── See references/advanced-patterns.md
├── Polymorphic or heterogeneous types ─── See references/advanced-patterns.md
├── Date, Data, or Float strategies ────── See "Encoder/Decoder Configuration"
├── TOON format encoding/decoding ──────── See references/toon-format.md
├── Migrating JSON ↔ TOON ─────────────── See references/toon-format.md
└── Debugging decode failures ──────────── See "Debugging Codable Errors"
When all stored properties are Codable, the compiler synthesizes everything:
struct User: Codable {
let id: Int
let name: String
let email: String?
let tags: [String]
let isActive: Bool
}
// Encode
let data = try JSONEncoder().encode(user)
// Decode
let user = try JSONDecoder().decode(User.self, from: data)
Synthesis works for struct, class, enum (with raw values or associated values in Swift 5.5+), and actor.
// Raw-value enum — encodes as the raw value
enum Priority: String, Codable {
case low, medium, high, critical
}
// Enum with associated values (Swift 5.5+)
enum PaymentMethod: Codable {
case creditCard(last4: String, expiry: String)
case bankTransfer(routingNumber: String)
case applePay
}
Map between Swift property names and serialized key names:
struct Article: Codable {
let id: Int
let title: String
let bodyText: String
let publishedAt: Date
let authorName: String
enum CodingKeys: String, CodingKey {
case id
case title
case bodyText = "body_text"
case publishedAt = "published_at"
case authorName = "author_name"
}
}
Convert all keys without manual CodingKeys:
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase // body_text → bodyText
let encoder = JSONEncoder()
encoder.keyEncodingStrategy = .convertToSnakeCase // bodyText → body_text
Custom key conversion:
decoder.keyDecodingStrategy = .custom { codingPath in
let lastKey = codingPath.last!.stringValue
// e.g., strip a prefix
let stripped = lastKey.hasPrefix("_") ? String(lastKey.dropFirst()) : lastKey
return AnyCodingKey(stringValue: stripped)!
}
let encoder = JSONEncoder()
// ISO 8601 (recommended for APIs)
encoder.dateEncodingStrategy = .iso8601
// Unix timestamp (seconds since epoch)
encoder.dateEncodingStrategy = .secondsSince1970
// Custom format
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
formatter.locale = Locale(identifier: "en_US_POSIX")
encoder.dateEncodingStrategy = .formatted(formatter)
// Full control
encoder.dateEncodingStrategy = .custom { date, encoder in
var container = encoder.singleValueContainer()
try container.encode(date.timeIntervalSince1970 * 1000) // milliseconds
}
The same strategies are available on JSONDecoder via dateDecodingStrategy.
encoder.dataEncodingStrategy = .base64 // default
encoder.dataEncodingStrategy = .deferredToData // raw bytes as [UInt8] array
encoder.dataEncodingStrategy = .custom { data, encoder in
var container = encoder.singleValueContainer()
try container.encode(data.map { String(format: "%02x", $0) }.joined())
}
// Handle Infinity and NaN
decoder.nonConformingFloatDecodingStrategy = .convertFromString(
positiveInfinity: "Infinity",
negativeInfinity: "-Infinity",
nan: "NaN"
)
encoder.outputFormatting = [.prettyPrinted, .sortedKeys, .withoutEscapingSlashes]
do {
let model = try JSONDecoder().decode(MyModel.self, from: data)
} catch let DecodingError.keyNotFound(key, context) {
print("Missing key: \(key.stringValue)")
print("Path: \(context.codingPath.map(\.stringValue).joined(separator: "."))")
} catch let DecodingError.typeMismatch(type, context) {
print("Expected \(type) at \(context.codingPath.map(\.stringValue).joined(separator: "."))")
print("Debug: \(context.debugDescription)")
} catch let DecodingError.valueNotFound(type, context) {
print("Null value for non-optional \(type) at \(context.codingPath.map(\.stringValue).joined(separator: "."))")
} catch let DecodingError.dataCorrupted(context) {
print("Corrupted data at \(context.codingPath.map(\.stringValue).joined(separator: "."))")
print("Debug: \(context.debugDescription)")
}
| Symptom | Cause | Fix |
|---|---|---|
keyNotFound | JSON uses snake_case, Swift uses camelCase | Add .convertFromSnakeCase or custom CodingKeys |
typeMismatch for Int | JSON value is "42" (string) | Decode as String and convert, or use a custom init(from:) |
typeMismatch for Bool | API sends 0/1 instead of true/false | Custom decode with Int fallback |
valueNotFound | Null in JSON for non-optional property | Make the property optional or provide a decodeIfPresent default |
dataCorrupted for Date | Date format doesn't match strategy | Check the strategy matches the API's format exactly |
| Silent empty results | Wrong root type (object vs. array) | Verify the top-level JSON structure |
struct Settings: Decodable {
let theme: String
let fontSize: Int
let notifications: Bool
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
theme = try container.decodeIfPresent(String.self, forKey: .theme) ?? "system"
fontSize = try container.decodeIfPresent(Int.self, forKey: .fontSize) ?? 14
notifications = try container.decodeIfPresent(Bool.self, forKey: .notifications) ?? true
}
enum CodingKeys: String, CodingKey {
case theme, fontSize, notifications
}
}
TOONEncoder/TOONDecoder usage, configuration, tabular arrays, key folding, and JSON-to-TOON migrationgenerators-networking-layer (API client with Codable), swiftdata-patterns (model layer)