{"id":"447e3fa7-e97b-4389-a876-439ef5cdc718","shortId":"gjB88n","kind":"skill","title":"swift-codable","tagline":"Implement Swift Codable models for JSON and property-list encoding and decoding with JSONDecoder, JSONEncoder, CodingKeys, and custom init(from:) or encode(to:). Use when parsing API responses, remapping keys, flattening nested JSON, handling date or data decoding strategies, dec","description":"# Swift Codable\n\nEncode and decode Swift types using `Codable` (`Encodable & Decodable`) with\n`JSONEncoder`, `JSONDecoder`, and related APIs. Targets Swift 6.3 / iOS 26+.\n\n## Contents\n\n- [Basic Conformance](#basic-conformance)\n- [Custom CodingKeys](#custom-codingkeys)\n- [Custom Decoding and Encoding](#custom-decoding-and-encoding)\n- [Nested and Flattened Containers](#nested-and-flattened-containers)\n- [Heterogeneous Arrays](#heterogeneous-arrays)\n- [Date Decoding Strategies](#date-decoding-strategies)\n- [Data and Key Strategies](#data-and-key-strategies)\n- [Lossy Array Decoding](#lossy-array-decoding)\n- [Single Value Containers](#single-value-containers)\n- [Default Values for Missing Keys](#default-values-for-missing-keys)\n- [Encoder and Decoder Configuration](#encoder-and-decoder-configuration)\n- [Codable with URLSession](#codable-with-urlsession)\n- [Codable with SwiftData](#codable-with-swiftdata)\n- [Codable with UserDefaults](#codable-with-userdefaults)\n- [Common Mistakes](#common-mistakes)\n- [Review Checklist](#review-checklist)\n- [References](#references)\n\n## Basic Conformance\n\nWhen all stored properties are themselves `Codable`, the compiler synthesizes\nconformance automatically:\n\n```swift\nstruct User: Codable {\n    let id: Int\n    let name: String\n    let email: String\n    let isVerified: Bool\n}\n\nlet user = try JSONDecoder().decode(User.self, from: jsonData)\nlet encoded = try JSONEncoder().encode(user)\n```\n\nPrefer `Decodable` for read-only API responses and `Encodable` for write-only.\nUse `Codable` only when both directions are required.\n\n## Custom CodingKeys\n\nRename JSON keys without writing a custom decoder by declaring a `CodingKeys`\nenum:\n\n```swift\nstruct Product: Codable {\n    let id: Int\n    let displayName: String\n    let imageURL: URL\n    let priceInCents: Int\n\n    enum CodingKeys: String, CodingKey {\n        case id\n        case displayName = \"display_name\"\n        case imageURL = \"image_url\"\n        case priceInCents = \"price_in_cents\"\n    }\n}\n```\n\nEvery stored property must appear in the enum. Omitting a property from\n`CodingKeys` excludes it from encoding/decoding -- provide a default value or\ncompute it separately.\n\n## Custom Decoding and Encoding\n\nOverride `init(from:)` and `encode(to:)` for transformations the synthesized\nconformance cannot handle:\n\n```swift\nstruct Event: Codable {\n    let name: String\n    let timestamp: Date\n    let tags: [String]\n\n    enum CodingKeys: String, CodingKey {\n        case name, timestamp, tags\n    }\n\n    init(from decoder: Decoder) throws {\n        let container = try decoder.container(keyedBy: CodingKeys.self)\n        name = try container.decode(String.self, forKey: .name)\n        // Decode Unix timestamp as Double, convert to Date\n        let epoch = try container.decode(Double.self, forKey: .timestamp)\n        timestamp = Date(timeIntervalSince1970: epoch)\n        // Default to empty array when key is missing\n        tags = try container.decodeIfPresent([String].self, forKey: .tags) ?? []\n    }\n\n    func encode(to encoder: Encoder) throws {\n        var container = encoder.container(keyedBy: CodingKeys.self)\n        try container.encode(name, forKey: .name)\n        try container.encode(timestamp.timeIntervalSince1970, forKey: .timestamp)\n        try container.encode(tags, forKey: .tags)\n    }\n}\n```\n\n## Nested and Flattened Containers\n\nUse `nestedContainer(keyedBy:forKey:)` to navigate and flatten nested JSON:\n\n```swift\n// JSON: { \"id\": 1, \"location\": { \"lat\": 37.7749, \"lng\": -122.4194 } }\nstruct Place: Decodable {\n    let id: Int\n    let latitude: Double\n    let longitude: Double\n\n    enum CodingKeys: String, CodingKey { case id, location }\n    enum LocationKeys: String, CodingKey { case lat, lng }\n\n    init(from decoder: Decoder) throws {\n        let container = try decoder.container(keyedBy: CodingKeys.self)\n        id = try container.decode(Int.self, forKey: .id)\n        let location = try container.nestedContainer(\n            keyedBy: LocationKeys.self, forKey: .location)\n        latitude = try location.decode(Double.self, forKey: .lat)\n        longitude = try location.decode(Double.self, forKey: .lng)\n    }\n}\n```\n\nChain multiple `nestedContainer` calls to flatten deeply nested structures.\nAlso use `nestedUnkeyedContainer(forKey:)` for nested arrays.\n\n## Heterogeneous Arrays\n\nDecode arrays of mixed types using a discriminator field:\n\n```swift\n// JSON: [{\"type\":\"text\",\"content\":\"Hello\"},{\"type\":\"image\",\"url\":\"pic.jpg\"}]\nenum ContentBlock: Decodable {\n    case text(String)\n    case image(URL)\n\n    enum CodingKeys: String, CodingKey { case type, content, url }\n\n    init(from decoder: Decoder) throws {\n        let container = try decoder.container(keyedBy: CodingKeys.self)\n        let type = try container.decode(String.self, forKey: .type)\n        switch type {\n        case \"text\":\n            let content = try container.decode(String.self, forKey: .content)\n            self = .text(content)\n        case \"image\":\n            let url = try container.decode(URL.self, forKey: .url)\n            self = .image(url)\n        default:\n            throw DecodingError.dataCorruptedError(\n                forKey: .type, in: container,\n                debugDescription: \"Unknown type: \\(type)\")\n        }\n    }\n}\n\nlet blocks = try JSONDecoder().decode([ContentBlock].self, from: jsonData)\n```\n\n## Date Decoding Strategies\n\nConfigure `JSONDecoder.dateDecodingStrategy` to match your API:\n\n```swift\nlet decoder = JSONDecoder()\n\n// ISO 8601 (e.g., \"2024-03-15T10:30:00Z\")\ndecoder.dateDecodingStrategy = .iso8601\n\n// Unix timestamp in seconds (e.g., 1710499800)\ndecoder.dateDecodingStrategy = .secondsSince1970\n\n// Custom DateFormatter\nlet formatter = DateFormatter()\nformatter.dateFormat = \"yyyy-MM-dd\"\nformatter.locale = Locale(identifier: \"en_US_POSIX\")\nformatter.timeZone = TimeZone(secondsFromGMT: 0)\ndecoder.dateDecodingStrategy = .formatted(formatter)\n\n// Custom closure for multiple formats\ndecoder.dateDecodingStrategy = .custom { decoder in\n    let container = try decoder.singleValueContainer()\n    let string = try container.decode(String.self)\n    if let date = ISO8601DateFormatter().date(from: string) { return date }\n    throw DecodingError.dataCorruptedError(\n        in: container, debugDescription: \"Cannot decode date: \\(string)\")\n}\n```\n\nSet the matching strategy on `JSONEncoder`:\n`encoder.dateEncodingStrategy = .iso8601`\n\n## Data and Key Strategies\n\n```swift\nlet decoder = JSONDecoder()\ndecoder.dataDecodingStrategy = .base64           // Base64-encoded Data fields\ndecoder.keyDecodingStrategy = .convertFromSnakeCase  // snake_case -> camelCase\n// {\"user_name\": \"Alice\"} maps to `var userName: String` -- no CodingKeys needed\n\nlet encoder = JSONEncoder()\nencoder.dataEncodingStrategy = .base64\nencoder.keyEncodingStrategy = .convertToSnakeCase\n```\n\n## Lossy Array Decoding\n\nBy default, one invalid element fails the entire array. Use a wrapper to skip\ninvalid elements:\n\n```swift\nstruct LossyArray<Element: Decodable>: Decodable {\n    let elements: [Element]\n\n    init(from decoder: Decoder) throws {\n        var container = try decoder.unkeyedContainer()\n        var elements: [Element] = []\n        while !container.isAtEnd {\n            if let element = try? container.decode(Element.self) {\n                elements.append(element)\n            } else {\n                _ = try? container.decode(AnyCodableValue.self) // advance past bad element\n            }\n        }\n        self.elements = elements\n    }\n}\nprivate struct AnyCodableValue: Decodable {}\n```\n\n## Single Value Containers\n\nWrap primitives for type safety using `singleValueContainer()`:\n\n```swift\nstruct UserID: Codable, Hashable {\n    let rawValue: String\n\n    init(_ rawValue: String) { self.rawValue = rawValue }\n\n    init(from decoder: Decoder) throws {\n        let container = try decoder.singleValueContainer()\n        rawValue = try container.decode(String.self)\n    }\n\n    func encode(to encoder: Encoder) throws {\n        var container = encoder.singleValueContainer()\n        try container.encode(rawValue)\n    }\n}\n// JSON: \"usr_abc123\" decodes directly to UserID\n```\n\n## Default Values for Missing Keys\n\nUse `decodeIfPresent` with nil-coalescing to provide defaults:\n\n```swift\nstruct Settings: Decodable {\n    let theme: String\n    let fontSize: Int\n    let notificationsEnabled: Bool\n\n    enum CodingKeys: String, CodingKey {\n        case theme, fontSize = \"font_size\"\n        case notificationsEnabled = \"notifications_enabled\"\n    }\n\n    init(from decoder: Decoder) throws {\n        let container = try decoder.container(keyedBy: CodingKeys.self)\n        theme = try container.decodeIfPresent(String.self, forKey: .theme) ?? \"system\"\n        fontSize = try container.decodeIfPresent(Int.self, forKey: .fontSize) ?? 16\n        notificationsEnabled = try container.decodeIfPresent(\n            Bool.self, forKey: .notificationsEnabled) ?? true\n    }\n}\n```\n\n## Encoder and Decoder Configuration\n\n```swift\nlet encoder = JSONEncoder()\nencoder.outputFormatting = [.prettyPrinted, .sortedKeys, .withoutEscapingSlashes]\n\n// Non-conforming floats (NaN, Infinity are not valid JSON)\nencoder.nonConformingFloatEncodingStrategy = .convertToString(\n    positiveInfinity: \"Infinity\", negativeInfinity: \"-Infinity\", nan: \"NaN\")\ndecoder.nonConformingFloatDecodingStrategy = .convertFromString(\n    positiveInfinity: \"Infinity\", negativeInfinity: \"-Infinity\", nan: \"NaN\")\n```\n\n### PropertyListEncoder / PropertyListDecoder\n\n```swift\nlet plistEncoder = PropertyListEncoder()\nplistEncoder.outputFormat = .xml  // or .binary\nlet data = try plistEncoder.encode(settings)\nlet decoded = try PropertyListDecoder().decode(Settings.self, from: data)\n```\n\n## Codable with URLSession\n\n```swift\nfunc fetchUser(id: Int) async throws -> User {\n    let url = URL(string: \"https://api.example.com/users/\\(id)\")!\n    let (data, response) = try await URLSession.shared.data(from: url)\n    guard let http = response as? HTTPURLResponse,\n          (200...299).contains(http.statusCode) else {\n        throw APIError.invalidResponse\n    }\n    let decoder = JSONDecoder()\n    decoder.keyDecodingStrategy = .convertFromSnakeCase\n    decoder.dateDecodingStrategy = .iso8601\n    return try decoder.decode(User.self, from: data)\n}\n\n// Generic API envelope for wrapped responses\nstruct APIResponse<T: Decodable>: Decodable {\n    let data: T\n    let meta: Meta?\n    struct Meta: Decodable { let page: Int; let totalPages: Int }\n}\nlet users = try decoder.decode(APIResponse<[User]>.self, from: data).data\n```\n\n## Codable with SwiftData\n\n`Codable` structs work as composite attributes in SwiftData models. In iOS 18+,\nSwiftData natively supports them without explicit `@Attribute(.transformable)`:\n\n```swift\nstruct Address: Codable {\n    var street: String\n    var city: String\n    var zipCode: String\n}\n\n@Model class Contact {\n    var name: String\n    var address: Address?  // Codable struct stored as composite attribute\n    init(name: String, address: Address? = nil) {\n        self.name = name; self.address = address\n    }\n}\n```\n\n## Codable with UserDefaults\n\nStore `Codable` values via `RawRepresentable` for `@AppStorage`:\n\n```swift\nstruct UserPreferences: Codable {\n    var showOnboarding: Bool = true\n    var accentColor: String = \"blue\"\n}\n\nextension UserPreferences: RawRepresentable {\n    init?(rawValue: String) {\n        guard let data = rawValue.data(using: .utf8),\n              let decoded = try? JSONDecoder().decode(Self.self, from: data)\n        else { return nil }\n        self = decoded\n    }\n    var rawValue: String {\n        guard let data = try? JSONEncoder().encode(self),\n              let string = String(data: data, encoding: .utf8)\n        else { return \"{}\" }\n        return string\n    }\n}\n\nstruct SettingsView: View {\n    @AppStorage(\"userPrefs\") private var prefs = UserPreferences()\n    var body: some View {\n        Toggle(\"Show Onboarding\", isOn: $prefs.showOnboarding)\n    }\n}\n```\n\n## Common Mistakes\n\n**1. Not handling missing optional keys:**\n```swift\n// DON'T -- crashes if key is absent\nlet value = try container.decode(String.self, forKey: .bio)\n// DO -- returns nil for missing keys\nlet value = try container.decodeIfPresent(String.self, forKey: .bio) ?? \"\"\n```\n\n**2. Failing entire array when one element is invalid:**\n```swift\n// DON'T -- one bad element kills the whole decode\nlet items = try container.decode([Item].self, forKey: .items)\n// DO -- use LossyArray or decode elements individually\nlet items = try container.decode(LossyArray<Item>.self, forKey: .items).elements\n```\n\n**3. Date strategy mismatch:**\n```swift\n// DON'T -- default strategy expects Double, but API sends ISO string\nlet decoder = JSONDecoder()  // dateDecodingStrategy defaults to .deferredToDate\n// DO -- set strategy to match your API format\ndecoder.dateDecodingStrategy = .iso8601\n```\n\n**4. Force-unwrapping decoded optionals:**\n```swift\n// DON'T\nlet user = try? decoder.decode(User.self, from: data)\nprint(user!.name)\n// DO\nguard let user = try? decoder.decode(User.self, from: data) else { return }\n```\n\n**5. Using Codable when only Decodable is needed:**\n```swift\n// DON'T -- unnecessarily constrains the type to also be Encodable\nstruct APIResponse: Codable { let id: Int; let message: String }\n// DO -- use Decodable for read-only API responses\nstruct APIResponse: Decodable { let id: Int; let message: String }\n```\n\n**6. Manual CodingKeys for simple snake_case APIs:**\n```swift\n// DON'T -- verbose boilerplate for every model\nenum CodingKeys: String, CodingKey {\n    case userName = \"user_name\"\n    case avatarUrl = \"avatar_url\"\n}\n// DO -- configure once on the decoder\ndecoder.keyDecodingStrategy = .convertFromSnakeCase\n```\n\n## Review Checklist\n\n- [ ] Types conform to `Decodable` only when encoding is not needed\n- [ ] `decodeIfPresent` used with defaults for optional or missing keys\n- [ ] `keyDecodingStrategy = .convertFromSnakeCase` used instead of manual CodingKeys for simple snake_case APIs\n- [ ] `dateDecodingStrategy` matches the API date format\n- [ ] Arrays of unreliable data use lossy decoding to skip invalid elements\n- [ ] Custom `init(from:)` validates and transforms data instead of post-decode fixups\n- [ ] `JSONEncoder.outputFormatting` includes `.sortedKeys` for deterministic test output\n- [ ] Wrapper types (UserID, etc.) use `singleValueContainer` for clean JSON\n- [ ] Generic `APIResponse<T>` wrapper used for consistent API envelope handling\n- [ ] No force-unwrapping of decoded values\n- [ ] `@AppStorage` Codable types conform to `RawRepresentable`\n- [ ] SwiftData composite attributes use `Codable` structs\n\n## References\n\n- [Codable](https://sosumi.ai/documentation/swift/codable/) -- protocol combining Encodable and Decodable\n- [JSONDecoder](https://sosumi.ai/documentation/foundation/jsondecoder/) -- decodes JSON data into Codable types\n- [JSONEncoder](https://sosumi.ai/documentation/foundation/jsonencoder/) -- encodes Codable types as JSON data\n- [CodingKey](https://sosumi.ai/documentation/swift/codingkey/) -- protocol for encoding/decoding keys\n- [Encoding and Decoding Custom Types](https://sosumi.ai/documentation/foundation/encoding-and-decoding-custom-types/) -- Apple guide on custom Codable conformance\n- [Using JSON with Custom Types](https://sosumi.ai/documentation/foundation/archives_and_serialization/using_json_with_custom_types/) -- Apple sample code for JSON patterns","tags":["swift","codable","ios","skills","dpearson2699","accessibility","agent-skills","ai-coding","apple","claude-code","codex-skills","cursor-skills"],"capabilities":["skill","source-dpearson2699","skill-swift-codable","topic-accessibility","topic-agent-skills","topic-ai-coding","topic-apple","topic-claude-code","topic-codex-skills","topic-cursor-skills","topic-ios","topic-ios-development","topic-liquid-glass","topic-localization","topic-mapkit"],"categories":["swift-ios-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/dpearson2699/swift-ios-skills/swift-codable","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add dpearson2699/swift-ios-skills","source_repo":"https://github.com/dpearson2699/swift-ios-skills","install_from":"skills.sh"}},"qualityScore":"0.700","qualityRationale":"deterministic score 0.70 from registry signals: · indexed on github topic:agent-skills · 599 github stars · SKILL.md body (15,346 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-18T18:53:44.778Z","embedding":null,"createdAt":"2026-04-18T20:37:20.925Z","updatedAt":"2026-05-18T18:53:44.778Z","lastSeenAt":"2026-05-18T18:53:44.778Z","tsv":"'-03':661 '-122.4194':462 '-15':662 '/documentation/foundation/archives_and_serialization/using_json_with_custom_types/)':1655 '/documentation/foundation/encoding-and-decoding-custom-types/)':1641 '/documentation/foundation/jsondecoder/)':1609 '/documentation/foundation/jsonencoder/)':1619 '/documentation/swift/codable/)':1600 '/documentation/swift/codingkey/)':1629 '/users/':1048 '0':695 '00z':665 '1':457,1267 '16':962 '1710499800':673 '18':1132 '2':1301 '200':1064 '2024':660 '26':66 '299':1065 '3':1344 '30':664 '37.7749':460 '4':1377 '5':1407 '6':1453 '6.3':64 '8601':658 'abc123':893 'absent':1280 'accentcolor':1198 'address':1143,1161,1162,1172,1173,1178 'advanc':833 'alic':765 'also':535,1423 'anycodablevalu':841 'anycodablevalue.self':832 'api':31,61,234,652,1085,1356,1373,1442,1460,1521,1525,1574 'api.example.com':1047 'api.example.com/users/':1046 'apierror.invalidresponse':1070 'apirespons':1091,1112,1427,1445,1569 'appear':304 'appl':1642,1656 'appstorag':1188,1250,1584 'array':97,100,118,122,402,541,543,545,782,792,1304,1528 'async':1039 'attribut':1126,1139,1168,1592 'automat':197 'avatar':1479 'avatarurl':1478 'await':1054 'bad':835,1314 'base64':752,754,778 'base64-encoded':753 'basic':68,71,184 'basic-conform':70 'binari':1017 'bio':1287,1300 'block':636 'blue':1200 'bodi':1257 'boilerpl':1465 'bool':213,924,1195 'bool.self':966 'call':529 'camelcas':762 'cannot':340,731 'case':285,287,291,295,359,479,486,566,569,576,600,612,761,929,934,1459,1473,1477,1520 'cent':299 'chain':526 'checklist':178,181,1490 'citi':1149 'class':1155 'clean':1566 'closur':700 'coalesc':908 'codabl':3,6,46,53,151,155,158,162,165,169,192,201,243,268,345,856,1031,1118,1121,1144,1163,1179,1183,1192,1409,1428,1585,1594,1597,1614,1621,1646 'codable-with-swiftdata':161 'codable-with-urlsess':154 'codable-with-userdefault':168 'code':1658 'codingkey':20,74,77,251,263,282,284,312,356,358,476,478,485,573,575,772,926,928,1455,1470,1472,1516,1626 'codingkeys.self':373,424,499,590,948 'combin':1602 'common':172,175,1265 'common-mistak':174 'compil':194 'composit':1125,1167,1591 'comput':322 'configur':145,150,647,973,1482 'conform':69,72,185,196,339,984,1492,1587,1647 'consist':1573 'constrain':1419 'contact':1156 'contain':90,95,126,130,369,421,443,495,586,630,709,729,813,845,872,886,944,1066 'container.decode':376,391,502,594,605,617,715,825,831,877,1284,1323,1338 'container.decodeifpresent':409,951,958,965,1297 'container.encode':426,431,436,889 'container.isatend':820 'container.nestedcontainer':509 'content':67,557,578,603,608,611 'contentblock':564,640 'convert':385 'convertfromsnakecas':759,1075,1488,1511 'convertfromstr':1001 'converttosnakecas':780 'converttostr':993 'crash':1276 'custom':22,73,76,78,83,250,258,325,676,699,705,1539,1637,1645,1651 'custom-codingkey':75 'custom-decoding-and-encod':82 'data':41,108,113,743,756,1019,1030,1051,1083,1094,1116,1117,1209,1220,1231,1239,1240,1392,1404,1531,1545,1612,1625 'data-and-key-strategi':112 'date':39,101,105,351,387,396,644,719,721,725,733,1345,1526 'date-decoding-strategi':104 'datedecodingstrategi':1363,1522 'dateformatt':677,680 'dd':685 'debugdescript':631,730 'dec':44 'declar':261 'decod':16,42,49,55,79,84,102,106,119,123,144,149,218,229,259,326,365,366,380,465,491,492,544,565,582,583,639,645,655,706,732,749,783,803,809,810,842,868,869,894,915,940,941,972,1024,1027,1072,1092,1101,1214,1217,1225,1319,1332,1361,1381,1412,1437,1446,1486,1494,1534,1550,1582,1605,1610,1636 'decodeifpres':904,1501 'decoder.container':371,497,588,946 'decoder.datadecodingstrategy':751 'decoder.datedecodingstrategy':666,674,696,704,1076,1375 'decoder.decode':1080,1111,1389,1401 'decoder.keydecodingstrategy':758,1074,1487 'decoder.nonconformingfloatdecodingstrategy':1000 'decoder.singlevaluecontainer':711,874 'decoder.unkeyedcontainer':815 'decodingerror.datacorruptederror':626,727 'deepli':532 'default':131,137,319,399,624,785,898,911,1351,1364,1504 'default-values-for-missing-key':136 'deferredtod':1366 'determinist':1556 'direct':247,895 'discrimin':551 'display':289 'displaynam':273,288 'doubl':384,471,474,1354 'double.self':392,517,523 'e.g':659,672 'element':788,799,805,806,817,818,823,828,836,838,1307,1315,1333,1343,1538 'element.self':826 'elements.append':827 'els':829,1068,1221,1243,1405 'email':209 'empti':401 'en':689 'enabl':937 'encod':14,26,47,54,81,86,142,147,223,226,237,328,333,415,417,418,755,775,880,882,883,970,976,1234,1241,1425,1497,1603,1620,1634 'encoder-and-decoder-configur':146 'encoder.container':422 'encoder.dataencodingstrategy':777 'encoder.dateencodingstrategy':741 'encoder.keyencodingstrategy':779 'encoder.nonconformingfloatencodingstrategy':992 'encoder.outputformatting':978 'encoder.singlevaluecontainer':887 'encoding/decoding':316,1632 'entir':791,1303 'enum':264,281,307,355,475,482,563,572,925,1469 'envelop':1086,1575 'epoch':389,398 'etc':1562 'event':344 'everi':300,1467 'exclud':313 'expect':1353 'explicit':1138 'extens':1201 'fail':789,1302 'fetchus':1036 'field':552,757 'fixup':1551 'flatten':35,89,94,442,451,531 'float':985 'font':932 'fontsiz':920,931,956,961 'forc':1379,1579 'force-unwrap':1378,1578 'forkey':378,393,412,428,433,438,447,504,512,518,524,538,596,607,619,627,953,960,967,1286,1299,1326,1341 'format':697,703,1374,1527 'formatt':679,698 'formatter.dateformat':681 'formatter.locale':686 'formatter.timezone':692 'func':414,879,1035 'generic':1084,1568 'guard':1058,1207,1229,1397 'guid':1643 'handl':38,341,1269,1576 'hashabl':857 'hello':558 'heterogen':96,99,542 'heterogeneous-array':98 'http':1060 'http.statuscode':1067 'httpurlrespons':1063 'id':203,270,286,456,467,480,500,505,1037,1049,1430,1448 'identifi':688 'imag':293,560,570,613,622 'imageurl':276,292 'implement':4 'includ':1553 'individu':1334 'infin':987,995,997,1003,1005 'init':23,330,363,489,580,807,861,866,938,1169,1204,1540 'instead':1513,1546 'int':204,271,280,468,921,1038,1104,1107,1431,1449 'int.self':503,959 'invalid':787,798,1309,1537 'io':65,1131 'iso':657,1358 'iso8601':667,742,1077,1376 'iso8601dateformatter':720 'ison':1263 'isverifi':212 'item':1321,1324,1327,1336,1342 'json':9,37,253,453,455,554,891,991,1567,1611,1624,1649,1660 'jsondata':221,643 'jsondecod':18,58,217,638,656,750,1073,1216,1362,1606 'jsondecoder.datedecodingstrategy':648 'jsonencod':19,57,225,740,776,977,1233,1616 'jsonencoder.outputformatting':1552 'key':34,110,115,135,141,254,404,745,902,1272,1278,1293,1509,1633 'keydecodingstrategi':1510 'keyedbi':372,423,446,498,510,589,947 'kill':1316 'lat':459,487,519 'latitud':470,514 'let':202,205,208,211,214,222,269,272,275,278,346,349,352,368,388,466,469,472,494,506,585,591,602,614,635,654,678,708,712,718,748,774,804,822,858,871,916,919,922,943,975,1011,1018,1023,1042,1050,1059,1071,1093,1096,1102,1105,1108,1208,1213,1230,1236,1281,1294,1320,1335,1360,1386,1398,1429,1432,1447,1450 'list':13 'lng':461,488,525 'local':687 'locat':458,481,507,513 'location.decode':516,522 'locationkey':483 'locationkeys.self':511 'longitud':473,520 'lossi':117,121,781,1533 'lossy-array-decod':120 'lossyarray':802,1330,1339 'manual':1454,1515 'map':766 'match':650,737,1371,1523 'messag':1433,1451 'meta':1097,1098,1100 'mismatch':1347 'miss':134,140,406,901,1270,1292,1508 'mistak':173,176,1266 'mix':547 'mm':684 'model':7,1129,1154,1468 'multipl':527,702 'must':303 'name':206,290,347,360,374,379,427,429,764,1158,1170,1176,1395,1476 'nan':986,998,999,1006,1007 'nativ':1134 'navig':449 'need':773,1414,1500 'negativeinfin':996,1004 'nest':36,87,92,440,452,533,540 'nested-and-flattened-contain':91 'nestedcontain':445,528 'nestedunkeyedcontain':537 'nil':907,1174,1223,1290 'nil-coalesc':906 'non':983 'non-conform':982 'notif':936 'notificationsen':923,935,963,968 'omit':308 'onboard':1262 'one':786,1306,1313 'option':1271,1382,1506 'output':1558 'overrid':329 'page':1103 'pars':30 'past':834 'pattern':1661 'pic.jpg':562 'place':464 'plistencod':1012 'plistencoder.encode':1021 'plistencoder.outputformat':1014 'positiveinfin':994,1002 'posix':691 'post':1549 'post-decod':1548 'pref':1254 'prefer':228 'prefs.showonboarding':1264 'prettyprint':979 'price':297 'priceinc':279,296 'primit':847 'print':1393 'privat':839,1252 'product':267 'properti':12,189,302,310 'property-list':11 'propertylistdecod':1009,1026 'propertylistencod':1008,1013 'protocol':1601,1630 'provid':317,910 'rawrepresent':1186,1203,1589 'rawvalu':859,862,865,875,890,1205,1227 'rawvalue.data':1210 'read':232,1440 'read-on':231,1439 'refer':182,183,1596 'relat':60 'remap':33 'renam':252 'requir':249 'respons':32,235,1052,1061,1089,1443 'return':724,1078,1222,1244,1245,1289,1406 'review':177,180,1489 'review-checklist':179 'safeti':850 'sampl':1657 'second':671 'secondsfromgmt':694 'secondssince1970':675 'self':411,609,621,641,1114,1224,1235,1325,1340 'self.address':1177 'self.elements':837 'self.name':1175 'self.rawvalue':864 'self.self':1218 'send':1357 'separ':324 'set':735,914,1022,1368 'settings.self':1028 'settingsview':1248 'show':1261 'showonboard':1194 'simpl':1457,1518 'singl':124,128,843 'single-value-contain':127 'singlevaluecontain':852,1564 'size':933 'skill' 'skill-swift-codable' 'skip':797,1536 'snake':760,1458,1519 'sortedkey':980,1554 'sosumi.ai':1599,1608,1618,1628,1640,1654 'sosumi.ai/documentation/foundation/archives_and_serialization/using_json_with_custom_types/)':1653 'sosumi.ai/documentation/foundation/encoding-and-decoding-custom-types/)':1639 'sosumi.ai/documentation/foundation/jsondecoder/)':1607 'sosumi.ai/documentation/foundation/jsonencoder/)':1617 'sosumi.ai/documentation/swift/codable/)':1598 'sosumi.ai/documentation/swift/codingkey/)':1627 'source-dpearson2699' 'store':188,301,1165,1182 'strategi':43,103,107,111,116,646,738,746,1346,1352,1369 'street':1146 'string':207,210,274,283,348,354,357,410,477,484,568,574,713,723,734,770,860,863,918,927,1045,1147,1150,1153,1159,1171,1199,1206,1228,1237,1238,1246,1359,1434,1452,1471 'string.self':377,595,606,716,878,952,1285,1298 'struct':199,266,343,463,801,840,854,913,1090,1099,1122,1142,1164,1190,1247,1426,1444,1595 'structur':534 'support':1135 'swift':2,5,45,50,63,198,265,342,454,553,653,747,800,853,912,974,1010,1034,1141,1189,1273,1310,1348,1383,1415,1461 'swift-cod':1 'swiftdata':160,164,1120,1128,1133,1590 'switch':598 'synthes':195,338 'system':955 't10':663 'tag':353,362,407,413,437,439 'target':62 'test':1557 'text':556,567,601,610 'theme':917,930,949,954 'throw':367,419,493,584,625,726,811,870,884,942,1040,1069 'timeintervalsince1970':397 'timestamp':350,361,382,394,395,434,669 'timestamp.timeintervalsince1970':432 'timezon':693 'toggl':1260 'topic-accessibility' 'topic-agent-skills' 'topic-ai-coding' 'topic-apple' 'topic-claude-code' 'topic-codex-skills' 'topic-cursor-skills' 'topic-ios' 'topic-ios-development' 'topic-liquid-glass' 'topic-localization' 'topic-mapkit' 'totalpag':1106 'transform':336,1140,1544 'tri':216,224,370,375,390,408,425,430,435,496,501,508,515,521,587,593,604,616,637,710,714,814,824,830,873,876,888,945,950,957,964,1020,1025,1053,1079,1110,1215,1232,1283,1296,1322,1337,1388,1400 'true':969,1196 'type':51,548,555,559,577,592,597,599,628,633,634,849,1421,1491,1560,1586,1615,1622,1638,1652 'unix':381,668 'unknown':632 'unnecessarili':1418 'unreli':1530 'unwrap':1380,1580 'url':277,294,561,571,579,615,620,623,1043,1044,1057,1480 'url.self':618 'urlsess':153,157,1033 'urlsession.shared.data':1055 'us':690 'use':28,52,242,444,536,549,793,851,903,1211,1329,1408,1436,1502,1512,1532,1563,1571,1593,1648 'user':200,215,227,763,1041,1109,1113,1387,1394,1399,1475 'user.self':219,1081,1390,1402 'userdefault':167,171,1181 'userid':855,897,1561 'usernam':769,1474 'userpref':1251 'userprefer':1191,1202,1255 'usr':892 'utf8':1212,1242 'valid':990,1542 'valu':125,129,132,138,320,844,899,1184,1282,1295,1583 'var':420,768,812,816,885,1145,1148,1151,1157,1160,1193,1197,1226,1253,1256 'verbos':1464 'via':1185 'view':1249,1259 'whole':1318 'without':255,1137 'withoutescapingslash':981 'work':1123 'wrap':846,1088 'wrapper':795,1559,1570 'write':240,256 'write-on':239 'xml':1015 'yyyi':683 'yyyy-mm-dd':682 'zipcod':1152","prices":[{"id":"da050b7c-b665-4669-8b0c-5909f101e97c","listingId":"447e3fa7-e97b-4389-a876-439ef5cdc718","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"dpearson2699","category":"swift-ios-skills","install_from":"skills.sh"},"createdAt":"2026-04-18T20:37:20.925Z"}],"sources":[{"listingId":"447e3fa7-e97b-4389-a876-439ef5cdc718","source":"github","sourceId":"dpearson2699/swift-ios-skills/swift-codable","sourceUrl":"https://github.com/dpearson2699/swift-ios-skills/tree/main/skills/swift-codable","isPrimary":false,"firstSeenAt":"2026-04-18T22:01:19.851Z","lastSeenAt":"2026-05-18T18:53:44.778Z"},{"listingId":"447e3fa7-e97b-4389-a876-439ef5cdc718","source":"skills_sh","sourceId":"dpearson2699/swift-ios-skills/swift-codable","sourceUrl":"https://skills.sh/dpearson2699/swift-ios-skills/swift-codable","isPrimary":true,"firstSeenAt":"2026-04-18T20:37:20.925Z","lastSeenAt":"2026-05-07T22:40:38.466Z"}],"details":{"listingId":"447e3fa7-e97b-4389-a876-439ef5cdc718","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"dpearson2699","slug":"swift-codable","github":{"repo":"dpearson2699/swift-ios-skills","stars":599,"topics":["accessibility","agent-skills","ai-coding","apple","claude-code","codex-skills","cursor-skills","ios","ios-development","liquid-glass","localization","mapkit","networking","storekit","swift","swift-concurrency","swiftdata","swiftui","widgetkit","xcode"],"license":"other","html_url":"https://github.com/dpearson2699/swift-ios-skills","pushed_at":"2026-04-26T21:04:17Z","description":"Agent Skills for iOS 26+, Swift 6.3, SwiftUI, and modern Apple frameworks","skill_md_sha":"0ac81735f89523595fc6914f8d060a2eed3299f0","skill_md_path":"skills/swift-codable/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/dpearson2699/swift-ios-skills/tree/main/skills/swift-codable"},"layout":"multi","source":"github","category":"swift-ios-skills","frontmatter":{"name":"swift-codable","description":"Implement Swift Codable models for JSON and property-list encoding and decoding with JSONDecoder, JSONEncoder, CodingKeys, and custom init(from:) or encode(to:). Use when parsing API responses, remapping keys, flattening nested JSON, handling date or data decoding strategies, decoding heterogeneous arrays, or integrating Codable with URLSession, SwiftData, or UserDefaults."},"skills_sh_url":"https://skills.sh/dpearson2699/swift-ios-skills/swift-codable"},"updatedAt":"2026-05-18T18:53:44.778Z"}}