@mmerterden/multi-agent-pipeline 20.6.0 → 20.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (326) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/LICENSE +0 -10
  3. package/docs/facts.json +1 -1
  4. package/manifest.json +328 -329
  5. package/package.json +2 -2
  6. package/pipeline/multi-agent-refs/features/design-conformance.md +62 -64
  7. package/pipeline/scripts/_notices.mjs +12 -1
  8. package/pipeline/scripts/gen-skills-index.mjs +1 -1
  9. package/pipeline/skills/.skill-manifest.json +106 -106
  10. package/pipeline/skills/shared/README.md +71 -71
  11. package/pipeline/skills/shared/external/agent-introspection-debugging/SKILL.md +1 -0
  12. package/pipeline/skills/shared/external/alarmkit/SKILL.md +373 -381
  13. package/pipeline/skills/shared/external/alarmkit/evals/evals.json +23 -18
  14. package/pipeline/skills/shared/external/alarmkit/references/alarmkit-patterns.md +328 -378
  15. package/pipeline/skills/shared/external/android-architecture/SKILL.md +2 -0
  16. package/pipeline/skills/shared/external/android-performance/SKILL.md +2 -0
  17. package/pipeline/skills/shared/external/android-security/SKILL.md +2 -0
  18. package/pipeline/skills/shared/external/app-clips/SKILL.md +260 -160
  19. package/pipeline/skills/shared/external/app-clips/evals/evals.json +27 -27
  20. package/pipeline/skills/shared/external/app-clips/references/data-handoff-notifications-location.md +150 -83
  21. package/pipeline/skills/shared/external/app-clips/references/routing-and-experiences.md +135 -83
  22. package/pipeline/skills/shared/external/app-clips/references/size-capabilities-and-promotion.md +143 -85
  23. package/pipeline/skills/shared/external/app-intents/SKILL.md +302 -304
  24. package/pipeline/skills/shared/external/app-intents/evals/evals.json +21 -21
  25. package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +594 -894
  26. package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +339 -277
  27. package/pipeline/skills/shared/external/app-store-optimization/evals/evals.json +27 -23
  28. package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +105 -122
  29. package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +143 -166
  30. package/pipeline/skills/shared/external/app-store-review/SKILL.md +307 -326
  31. package/pipeline/skills/shared/external/app-store-review/evals/evals.json +21 -21
  32. package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +105 -67
  33. package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +114 -101
  34. package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +333 -360
  35. package/pipeline/skills/shared/external/apple-on-device-ai/evals/evals.json +24 -27
  36. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +215 -322
  37. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +161 -256
  38. package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +277 -387
  39. package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +196 -210
  40. package/pipeline/skills/shared/external/authentication/SKILL.md +265 -381
  41. package/pipeline/skills/shared/external/authentication/evals/evals.json +25 -25
  42. package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +133 -178
  43. package/pipeline/skills/shared/external/authentication/references/passkeys.md +111 -147
  44. package/pipeline/skills/shared/external/avkit/SKILL.md +267 -364
  45. package/pipeline/skills/shared/external/avkit/evals/evals.json +26 -26
  46. package/pipeline/skills/shared/external/avkit/references/avkit-patterns.md +375 -493
  47. package/pipeline/skills/shared/external/background-processing/SKILL.md +270 -382
  48. package/pipeline/skills/shared/external/background-processing/evals/evals.json +22 -22
  49. package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +169 -317
  50. package/pipeline/skills/shared/external/backlog/BACKLOG.md +1 -1
  51. package/pipeline/skills/shared/external/backlog/SKILL.md +56 -33
  52. package/pipeline/skills/shared/external/callkit-voip/SKILL.md +290 -371
  53. package/pipeline/skills/shared/external/callkit-voip/evals/evals.json +24 -24
  54. package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +175 -343
  55. package/pipeline/skills/shared/external/ci-cd-pipelines/SKILL.md +1 -0
  56. package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +292 -381
  57. package/pipeline/skills/shared/external/cloudkit-sync/evals/evals.json +33 -30
  58. package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +227 -355
  59. package/pipeline/skills/shared/external/compose-components/SKILL.md +2 -0
  60. package/pipeline/skills/shared/external/compose-navigation/SKILL.md +3 -2
  61. package/pipeline/skills/shared/external/compose-testing/SKILL.md +2 -0
  62. package/pipeline/skills/shared/external/contacts-framework/SKILL.md +197 -346
  63. package/pipeline/skills/shared/external/contacts-framework/evals/evals.json +19 -21
  64. package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +169 -308
  65. package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +226 -376
  66. package/pipeline/skills/shared/external/core-bluetooth/evals/evals.json +25 -22
  67. package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +257 -337
  68. package/pipeline/skills/shared/external/core-data/SKILL.md +292 -368
  69. package/pipeline/skills/shared/external/core-data/evals/evals.json +30 -27
  70. package/pipeline/skills/shared/external/core-motion/SKILL.md +235 -324
  71. package/pipeline/skills/shared/external/core-motion/evals/evals.json +31 -27
  72. package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +210 -310
  73. package/pipeline/skills/shared/external/core-nfc/SKILL.md +292 -366
  74. package/pipeline/skills/shared/external/core-nfc/evals/evals.json +22 -24
  75. package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +233 -329
  76. package/pipeline/skills/shared/external/coreml/SKILL.md +323 -367
  77. package/pipeline/skills/shared/external/coreml/evals/evals.json +24 -21
  78. package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +562 -565
  79. package/pipeline/skills/shared/external/council/SKILL.md +1 -0
  80. package/pipeline/skills/shared/external/cryptokit/SKILL.md +253 -394
  81. package/pipeline/skills/shared/external/cryptokit/evals/evals.json +20 -18
  82. package/pipeline/skills/shared/external/cryptokit/references/cryptokit-patterns.md +299 -488
  83. package/pipeline/skills/shared/external/css-modern/SKILL.md +1 -0
  84. package/pipeline/skills/shared/external/database-patterns/SKILL.md +1 -0
  85. package/pipeline/skills/shared/external/debugging-instruments/SKILL.md +270 -323
  86. package/pipeline/skills/shared/external/debugging-instruments/evals/evals.json +27 -30
  87. package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +167 -315
  88. package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +140 -193
  89. package/pipeline/skills/shared/external/device-integrity/SKILL.md +230 -353
  90. package/pipeline/skills/shared/external/device-integrity/evals/evals.json +25 -21
  91. package/pipeline/skills/shared/external/device-integrity/references/device-integrity-patterns.md +159 -197
  92. package/pipeline/skills/shared/external/energykit/SKILL.md +225 -392
  93. package/pipeline/skills/shared/external/energykit/evals/evals.json +29 -28
  94. package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +174 -470
  95. package/pipeline/skills/shared/external/eventkit-calendar/SKILL.md +261 -383
  96. package/pipeline/skills/shared/external/eventkit-calendar/evals/evals.json +25 -22
  97. package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +165 -268
  98. package/pipeline/skills/shared/external/evidence-github/SKILL.md +2 -0
  99. package/pipeline/skills/shared/external/evidence-registry/SKILL.md +2 -0
  100. package/pipeline/skills/shared/external/gradle-kotlin-dsl/SKILL.md +2 -0
  101. package/pipeline/skills/shared/external/healthkit/SKILL.md +252 -303
  102. package/pipeline/skills/shared/external/healthkit/evals/evals.json +24 -23
  103. package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +369 -523
  104. package/pipeline/skills/shared/external/homekit-matter/SKILL.md +233 -348
  105. package/pipeline/skills/shared/external/homekit-matter/evals/evals.json +27 -22
  106. package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +199 -305
  107. package/pipeline/skills/shared/external/html-semantic/SKILL.md +1 -0
  108. package/pipeline/skills/shared/external/humanizer/SKILL.md +1 -0
  109. package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +368 -340
  110. package/pipeline/skills/shared/external/ios-accessibility/evals/evals.json +28 -27
  111. package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +314 -260
  112. package/pipeline/skills/shared/external/ios-accessibility/references/media-accessibility.md +97 -67
  113. package/pipeline/skills/shared/external/ios-accessibility/references/nutrition-labels.md +165 -101
  114. package/pipeline/skills/shared/external/ios-coding-standard/SKILL.md +1 -0
  115. package/pipeline/skills/shared/external/ios-localization/SKILL.md +258 -371
  116. package/pipeline/skills/shared/external/ios-localization/evals/evals.json +23 -23
  117. package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +283 -491
  118. package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +313 -440
  119. package/pipeline/skills/shared/external/ios-module-structure/SKILL.md +1 -0
  120. package/pipeline/skills/shared/external/ios-networking/SKILL.md +265 -341
  121. package/pipeline/skills/shared/external/ios-networking/evals/evals.json +24 -24
  122. package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +425 -652
  123. package/pipeline/skills/shared/external/ios-networking/references/file-storage-patterns.md +143 -285
  124. package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +93 -53
  125. package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +231 -456
  126. package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +517 -784
  127. package/pipeline/skills/shared/external/ios-security/SKILL.md +2 -0
  128. package/pipeline/skills/shared/external/ios-simulator/SKILL.md +265 -393
  129. package/pipeline/skills/shared/external/ios-simulator/evals/evals.json +21 -21
  130. package/pipeline/skills/shared/external/ios-simulator/references/simctl-commands.md +177 -270
  131. package/pipeline/skills/shared/external/live-activities/SKILL.md +318 -360
  132. package/pipeline/skills/shared/external/live-activities/evals/evals.json +21 -21
  133. package/pipeline/skills/shared/external/live-activities/references/activitykit-patterns.md +478 -710
  134. package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +91 -283
  135. package/pipeline/skills/shared/external/localization-reuse-map/reference/format-and-output.md +119 -151
  136. package/pipeline/skills/shared/external/localization-reuse-map/reference/publish-and-snapshot.md +60 -90
  137. package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md +119 -156
  138. package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-artifact.py +726 -787
  139. package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-spreadsheet.py +253 -288
  140. package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-annotations.py +243 -304
  141. package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-legacy-labels.py +88 -104
  142. package/pipeline/skills/shared/external/localization-reuse-map/scripts/publish-confluence.py +181 -235
  143. package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-key-shots.py +198 -263
  144. package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-overlay.py +461 -466
  145. package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-legacy-values.py +145 -151
  146. package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-new-values.py +123 -141
  147. package/pipeline/skills/shared/external/localization-reuse-map/scripts/scan-screen-keys.py +146 -157
  148. package/pipeline/skills/shared/external/localization-reuse-map/scripts/snapshot-resources.sh +22 -19
  149. package/pipeline/skills/shared/external/localization-reuse-map/scripts/verify-map.py +156 -140
  150. package/pipeline/skills/shared/external/mapkit-location/SKILL.md +295 -267
  151. package/pipeline/skills/shared/external/mapkit-location/evals/evals.json +28 -24
  152. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-corelocation-patterns.md +378 -532
  153. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +397 -499
  154. package/pipeline/skills/shared/external/metrickit-diagnostics/SKILL.md +165 -348
  155. package/pipeline/skills/shared/external/metrickit-diagnostics/evals/evals.json +26 -23
  156. package/pipeline/skills/shared/external/metrickit-diagnostics/references/metrickit-patterns.md +123 -130
  157. package/pipeline/skills/shared/external/musickit-audio/SKILL.md +189 -315
  158. package/pipeline/skills/shared/external/musickit-audio/evals/evals.json +22 -21
  159. package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +181 -270
  160. package/pipeline/skills/shared/external/natural-language/SKILL.md +188 -340
  161. package/pipeline/skills/shared/external/natural-language/evals/evals.json +21 -21
  162. package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +171 -225
  163. package/pipeline/skills/shared/external/nextjs-app-router/SKILL.md +1 -0
  164. package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +258 -392
  165. package/pipeline/skills/shared/external/passkit-wallet/evals/evals.json +30 -29
  166. package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +164 -231
  167. package/pipeline/skills/shared/external/pdfkit/SKILL.md +312 -344
  168. package/pipeline/skills/shared/external/pdfkit/evals/evals.json +19 -19
  169. package/pipeline/skills/shared/external/pdfkit/references/pdfkit-patterns.md +413 -624
  170. package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +242 -358
  171. package/pipeline/skills/shared/external/pencilkit-drawing/evals/evals.json +25 -21
  172. package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +161 -226
  173. package/pipeline/skills/shared/external/permissionkit/SKILL.md +282 -400
  174. package/pipeline/skills/shared/external/permissionkit/evals/evals.json +27 -30
  175. package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +237 -350
  176. package/pipeline/skills/shared/external/photos-camera-media/SKILL.md +276 -325
  177. package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +299 -545
  178. package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +344 -588
  179. package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +316 -660
  180. package/pipeline/skills/shared/external/photos-camera-media/references/photokit-patterns.md +270 -416
  181. package/pipeline/skills/shared/external/play-store-review/SKILL.md +2 -0
  182. package/pipeline/skills/shared/external/push-notifications/SKILL.md +312 -340
  183. package/pipeline/skills/shared/external/push-notifications/evals/evals.json +27 -26
  184. package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +328 -485
  185. package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +327 -560
  186. package/pipeline/skills/shared/external/python-patterns/SKILL.md +1 -0
  187. package/pipeline/skills/shared/external/react-best-practices/SKILL.md +1 -0
  188. package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +218 -410
  189. package/pipeline/skills/shared/external/realitykit-ar/evals/evals.json +24 -27
  190. package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +221 -348
  191. package/pipeline/skills/shared/external/rest-api-design/SKILL.md +1 -0
  192. package/pipeline/skills/shared/external/retrofit-networking/SKILL.md +2 -0
  193. package/pipeline/skills/shared/external/room-database/SKILL.md +2 -0
  194. package/pipeline/skills/shared/external/search-first/SKILL.md +1 -0
  195. package/pipeline/skills/shared/external/shareplay-activities/SKILL.md +222 -393
  196. package/pipeline/skills/shared/external/shareplay-activities/evals/evals.json +23 -24
  197. package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +280 -420
  198. package/pipeline/skills/shared/external/signal-community/SKILL.md +2 -0
  199. package/pipeline/skills/shared/external/skill-creator/SKILL.md +80 -41
  200. package/pipeline/skills/shared/external/skill-creator/audit.md +63 -59
  201. package/pipeline/skills/shared/external/skill-creator/checklist.md +28 -20
  202. package/pipeline/skills/shared/external/skill-creator/examples.md +40 -40
  203. package/pipeline/skills/shared/external/skill-creator/label-check.md +48 -36
  204. package/pipeline/skills/shared/external/skill-creator/scripts/audit-panel.js +91 -100
  205. package/pipeline/skills/shared/external/skill-creator/template.md +51 -39
  206. package/pipeline/skills/shared/external/speech-recognition/SKILL.md +217 -421
  207. package/pipeline/skills/shared/external/speech-recognition/evals/evals.json +23 -26
  208. package/pipeline/skills/shared/external/speech-recognition/references/speechanalyzer-patterns.md +133 -125
  209. package/pipeline/skills/shared/external/storekit/SKILL.md +228 -204
  210. package/pipeline/skills/shared/external/storekit/evals/evals.json +27 -24
  211. package/pipeline/skills/shared/external/storekit/references/app-review-guidelines.md +98 -109
  212. package/pipeline/skills/shared/external/storekit/references/core-patterns.md +298 -242
  213. package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +356 -649
  214. package/pipeline/skills/shared/external/swift-api-design-guidelines/SKILL.md +274 -399
  215. package/pipeline/skills/shared/external/swift-api-design-guidelines/evals/evals.json +22 -24
  216. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/argument-labels-and-parameters.md +107 -108
  217. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md +93 -165
  218. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md +99 -137
  219. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md +77 -120
  220. package/pipeline/skills/shared/external/swift-architecture/SKILL.md +334 -350
  221. package/pipeline/skills/shared/external/swift-architecture/evals/evals.json +22 -22
  222. package/pipeline/skills/shared/external/swift-charts/SKILL.md +208 -394
  223. package/pipeline/skills/shared/external/swift-charts/evals/evals.json +27 -30
  224. package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +351 -762
  225. package/pipeline/skills/shared/external/swift-codable/SKILL.md +339 -343
  226. package/pipeline/skills/shared/external/swift-codable/evals/evals.json +20 -20
  227. package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +303 -351
  228. package/pipeline/skills/shared/external/swift-concurrency/evals/evals.json +27 -24
  229. package/pipeline/skills/shared/external/swift-concurrency/references/approachable-concurrency.md +65 -80
  230. package/pipeline/skills/shared/external/swift-concurrency/references/async-algorithms.md +48 -84
  231. package/pipeline/skills/shared/external/swift-concurrency/references/bridging-interop.md +134 -79
  232. package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +145 -167
  233. package/pipeline/skills/shared/external/swift-concurrency/references/diagnostics.md +62 -50
  234. package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +92 -121
  235. package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +177 -241
  236. package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +258 -234
  237. package/pipeline/skills/shared/external/swift-language/SKILL.md +342 -382
  238. package/pipeline/skills/shared/external/swift-language/evals/evals.json +24 -27
  239. package/pipeline/skills/shared/external/swift-language/references/swift-attributes-interop.md +79 -56
  240. package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +297 -340
  241. package/pipeline/skills/shared/external/swift-security/SKILL.md +180 -161
  242. package/pipeline/skills/shared/external/swift-security/evals/evals.json +25 -25
  243. package/pipeline/skills/shared/external/swift-security/references/biometric-authentication.md +314 -469
  244. package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +408 -476
  245. package/pipeline/skills/shared/external/swift-security/references/common-anti-patterns.md +260 -530
  246. package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md +270 -477
  247. package/pipeline/skills/shared/external/swift-security/references/credential-storage-patterns.md +573 -571
  248. package/pipeline/skills/shared/external/swift-security/references/cryptokit-public-key.md +370 -441
  249. package/pipeline/skills/shared/external/swift-security/references/cryptokit-symmetric.md +332 -433
  250. package/pipeline/skills/shared/external/swift-security/references/keychain-access-control.md +346 -468
  251. package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +352 -472
  252. package/pipeline/skills/shared/external/swift-security/references/keychain-item-classes.md +431 -432
  253. package/pipeline/skills/shared/external/swift-security/references/keychain-sharing.md +328 -425
  254. package/pipeline/skills/shared/external/swift-security/references/migration-legacy-stores.md +341 -579
  255. package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +396 -457
  256. package/pipeline/skills/shared/external/swift-security/references/testing-security-code.md +354 -614
  257. package/pipeline/skills/shared/external/swift-testing/SKILL.md +188 -175
  258. package/pipeline/skills/shared/external/swift-testing/evals/evals.json +26 -24
  259. package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +80 -84
  260. package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +317 -433
  261. package/pipeline/skills/shared/external/swiftdata/SKILL.md +392 -256
  262. package/pipeline/skills/shared/external/swiftdata/evals/evals.json +24 -24
  263. package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +206 -402
  264. package/pipeline/skills/shared/external/swiftdata/references/indexing.md +59 -52
  265. package/pipeline/skills/shared/external/swiftdata/references/predicate-pitfalls.md +57 -33
  266. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +354 -747
  267. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +300 -508
  268. package/pipeline/skills/shared/external/swiftlint/SKILL.md +175 -226
  269. package/pipeline/skills/shared/external/swiftlint/references/adoption-and-configuration.md +141 -208
  270. package/pipeline/skills/shared/external/swiftlint/references/custom-rules-and-analyze.md +100 -109
  271. package/pipeline/skills/shared/external/swiftlint/references/plugins-run-scripts-and-integrations.md +159 -179
  272. package/pipeline/skills/shared/external/swiftlint/references/rule-reference.md +383 -18
  273. package/pipeline/skills/shared/external/swiftlint/references/rules-suppressions-and-baselines.md +143 -229
  274. package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +283 -366
  275. package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +396 -608
  276. package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +336 -385
  277. package/pipeline/skills/shared/external/swiftui-gestures/SKILL.md +239 -349
  278. package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +228 -310
  279. package/pipeline/skills/shared/external/swiftui-layout-components/SKILL.md +260 -249
  280. package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +92 -74
  281. package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +112 -177
  282. package/pipeline/skills/shared/external/swiftui-layout-components/references/list.md +61 -64
  283. package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +94 -134
  284. package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +193 -225
  285. package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +173 -327
  286. package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +193 -168
  287. package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +127 -150
  288. package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +132 -133
  289. package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +152 -117
  290. package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +106 -140
  291. package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +316 -252
  292. package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +341 -332
  293. package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +547 -854
  294. package/pipeline/skills/shared/external/swiftui-patterns/references/design-polish.md +485 -537
  295. package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +417 -499
  296. package/pipeline/skills/shared/external/swiftui-performance/SKILL.md +213 -376
  297. package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +86 -175
  298. package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +89 -195
  299. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +95 -182
  300. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-improving-swiftui-performance.md +71 -149
  301. package/pipeline/skills/shared/external/swiftui-performance/references/wwdc-session-sources.md +21 -27
  302. package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +303 -295
  303. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +204 -387
  304. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +469 -683
  305. package/pipeline/skills/shared/external/swiftui-webkit/SKILL.md +140 -186
  306. package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +75 -86
  307. package/pipeline/skills/shared/external/swiftui-webkit/references/local-content-and-custom-schemes.md +63 -60
  308. package/pipeline/skills/shared/external/swiftui-webkit/references/migration-and-fallbacks.md +69 -137
  309. package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +95 -67
  310. package/pipeline/skills/shared/external/tailwind-css/SKILL.md +1 -0
  311. package/pipeline/skills/shared/external/testing-backend/SKILL.md +1 -0
  312. package/pipeline/skills/shared/external/tipkit/SKILL.md +220 -335
  313. package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +356 -494
  314. package/pipeline/skills/shared/external/typescript-patterns/SKILL.md +1 -0
  315. package/pipeline/skills/shared/external/vision-framework/SKILL.md +260 -375
  316. package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +393 -515
  317. package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +363 -539
  318. package/pipeline/skills/shared/external/vue-composition/SKILL.md +1 -0
  319. package/pipeline/skills/shared/external/weatherkit/SKILL.md +152 -310
  320. package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +288 -407
  321. package/pipeline/skills/shared/external/web-accessibility/SKILL.md +1 -0
  322. package/pipeline/skills/shared/external/web-performance/SKILL.md +1 -0
  323. package/pipeline/skills/shared/external/web-testing/SKILL.md +1 -0
  324. package/pipeline/skills/shared/external/widgetkit/SKILL.md +216 -288
  325. package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +414 -719
  326. package/pipeline/skills/shared/external/NOTICE-swift-ios-skills.md +0 -39
@@ -1,496 +1,492 @@
1
1
  ---
2
2
  name: swift-codable
3
- 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."
3
+ description: "Codable JSON and property-list models: JSONDecoder, JSONEncoder, CodingKeys, custom init(from:)/encode(to:), nested containers, date, data and key strategies, heterogeneous and lossy arrays, missing-key defaults. Use when parsing API responses, remapping keys, flattening nested JSON, choosing decoding strategies, or wiring Codable into URLSession, SwiftData or UserDefaults."
4
4
  metadata:
5
- source: "dpearson2699/swift-ios-skills (PolyForm Perimeter 1.0.0)"
5
+ source: multi-agent-pipeline
6
6
  ---
7
7
 
8
8
  # Swift Codable
9
9
 
10
- Encode and decode Swift types using `Codable` (`Encodable & Decodable`) with
11
- `JSONEncoder`, `JSONDecoder`, and related APIs. Targets Swift 6.3 / iOS 26+.
12
-
13
- ## Contents
14
-
15
- - [Basic Conformance](#basic-conformance)
16
- - [Custom CodingKeys](#custom-codingkeys)
17
- - [Custom Decoding and Encoding](#custom-decoding-and-encoding)
18
- - [Nested and Flattened Containers](#nested-and-flattened-containers)
19
- - [Heterogeneous Arrays](#heterogeneous-arrays)
20
- - [Date Decoding Strategies](#date-decoding-strategies)
21
- - [Data and Key Strategies](#data-and-key-strategies)
22
- - [Lossy Array Decoding](#lossy-array-decoding)
23
- - [Single Value Containers](#single-value-containers)
24
- - [Default Values for Missing Keys](#default-values-for-missing-keys)
25
- - [Encoder and Decoder Configuration](#encoder-and-decoder-configuration)
26
- - [Codable with URLSession](#codable-with-urlsession)
27
- - [Codable with SwiftData](#codable-with-swiftdata)
28
- - [Codable with UserDefaults](#codable-with-userdefaults)
29
- - [Common Mistakes](#common-mistakes)
30
- - [Review Checklist](#review-checklist)
31
- - [References](#references)
32
-
33
- ## Basic Conformance
34
-
35
- When all stored properties are themselves `Codable`, the compiler synthesizes
36
- conformance automatically:
10
+ How to turn JSON and property lists into Swift values and back, for Swift 6.3
11
+ and iOS 26. The compiler writes most of the code; this skill is about the
12
+ places where it cannot, and about the boundaries where Codable meets
13
+ networking, SwiftData and `@AppStorage`.
14
+
15
+ ## Basic conformance
16
+
17
+ When every stored property is itself Codable, the compiler synthesizes the
18
+ whole conformance.
37
19
 
38
20
  ```swift
39
- struct User: Codable {
21
+ struct Recipe: Codable {
40
22
  let id: Int
41
- let name: String
42
- let email: String
43
- let isVerified: Bool
23
+ let title: String
24
+ let servings: Int
44
25
  }
45
26
 
46
- let user = try JSONDecoder().decode(User.self, from: jsonData)
47
- let encoded = try JSONEncoder().encode(user)
27
+ let recipe = try JSONDecoder().decode(Recipe.self, from: payload)
28
+ let bytes = try JSONEncoder().encode(recipe)
48
29
  ```
49
30
 
50
- Prefer `Decodable` for read-only API responses and `Encodable` for write-only.
51
- Use `Codable` only when both directions are required.
31
+ Pick the narrowest protocol:
32
+
33
+ - `Decodable` for data you only read, such as most API responses.
34
+ - `Encodable` for data you only send.
35
+ - `Codable` only when the type really travels both ways.
52
36
 
53
37
  ## Custom CodingKeys
54
38
 
55
- Rename JSON keys without writing a custom decoder by declaring a `CodingKeys`
56
- enum:
39
+ A `CodingKeys` enum renames keys and still leaves decoding to the compiler.
57
40
 
58
41
  ```swift
59
- struct Product: Codable {
42
+ struct Author: Decodable {
60
43
  let id: Int
61
- let displayName: String
62
- let imageURL: URL
63
- let priceInCents: Int
44
+ let penName: String
45
+ let avatarURL: URL
64
46
 
65
47
  enum CodingKeys: String, CodingKey {
66
48
  case id
67
- case displayName = "display_name"
68
- case imageURL = "image_url"
69
- case priceInCents = "price_in_cents"
49
+ case penName = "pen_name"
50
+ case avatarURL = "avatar_url"
70
51
  }
71
52
  }
72
53
  ```
73
54
 
74
- Every stored property must appear in the enum. Omitting a property from
75
- `CodingKeys` excludes it from encoding/decoding -- provide a default value or
76
- compute it separately.
55
+ Once the enum exists, it is the complete list of coded properties. A property
56
+ left out is not decoded or encoded at all, so it must have a default value or
57
+ be computed some other way.
77
58
 
78
- ## Custom Decoding and Encoding
59
+ ## Custom decoding and encoding
79
60
 
80
- Override `init(from:)` and `encode(to:)` for transformations the synthesized
81
- conformance cannot handle:
61
+ Write `init(from:)` and `encode(to:)` when a value needs converting on the
62
+ way in or out.
82
63
 
83
64
  ```swift
84
- struct Event: Codable {
85
- let name: String
86
- let timestamp: Date
87
- let tags: [String]
65
+ struct Shipment: Codable {
66
+ let reference: String
67
+ let dispatchedAt: Date
68
+ let labels: [String]
88
69
 
89
70
  enum CodingKeys: String, CodingKey {
90
- case name, timestamp, tags
71
+ case reference, dispatchedAt = "dispatched_epoch", labels
91
72
  }
92
73
 
93
- init(from decoder: Decoder) throws {
94
- let container = try decoder.container(keyedBy: CodingKeys.self)
95
- name = try container.decode(String.self, forKey: .name)
96
- // Decode Unix timestamp as Double, convert to Date
97
- let epoch = try container.decode(Double.self, forKey: .timestamp)
98
- timestamp = Date(timeIntervalSince1970: epoch)
99
- // Default to empty array when key is missing
100
- tags = try container.decodeIfPresent([String].self, forKey: .tags) ?? []
74
+ init(from decoder: any Decoder) throws {
75
+ let fields = try decoder.container(keyedBy: CodingKeys.self)
76
+ reference = try fields.decode(String.self, forKey: .reference)
77
+ let seconds = try fields.decode(Double.self, forKey: .dispatchedAt)
78
+ dispatchedAt = Date(timeIntervalSince1970: seconds)
79
+ labels = try fields.decodeIfPresent([String].self, forKey: .labels) ?? []
101
80
  }
102
81
 
103
- func encode(to encoder: Encoder) throws {
104
- var container = encoder.container(keyedBy: CodingKeys.self)
105
- try container.encode(name, forKey: .name)
106
- try container.encode(timestamp.timeIntervalSince1970, forKey: .timestamp)
107
- try container.encode(tags, forKey: .tags)
82
+ func encode(to encoder: any Encoder) throws {
83
+ var fields = encoder.container(keyedBy: CodingKeys.self)
84
+ try fields.encode(reference, forKey: .reference)
85
+ try fields.encode(dispatchedAt.timeIntervalSince1970, forKey: .dispatchedAt)
86
+ try fields.encode(labels, forKey: .labels)
108
87
  }
109
88
  }
110
89
  ```
111
90
 
112
- ## Nested and Flattened Containers
91
+ Doing the conversion here, instead of patching values after decoding, keeps
92
+ invalid instances from ever existing.
113
93
 
114
- Use `nestedContainer(keyedBy:forKey:)` to navigate and flatten nested JSON:
94
+ ## Nested and flattened containers
95
+
96
+ JSON often nests what the app wants flat. `nestedContainer(keyedBy:forKey:)`
97
+ reaches into the inner object with its own key enum.
115
98
 
116
99
  ```swift
117
- // JSON: { "id": 1, "location": { "lat": 37.7749, "lng": -122.4194 } }
118
- struct Place: Decodable {
119
- let id: Int
100
+ // { "name": "Harbour Cafe", "geo": { "latitude": 41.0, "longitude": 29.0 } }
101
+ struct Venue: Decodable {
102
+ let name: String
120
103
  let latitude: Double
121
104
  let longitude: Double
122
105
 
123
- enum CodingKeys: String, CodingKey { case id, location }
124
- enum LocationKeys: String, CodingKey { case lat, lng }
106
+ enum OuterKeys: String, CodingKey { case name, geo }
107
+ enum GeoKeys: String, CodingKey { case latitude, longitude }
125
108
 
126
- init(from decoder: Decoder) throws {
127
- let container = try decoder.container(keyedBy: CodingKeys.self)
128
- id = try container.decode(Int.self, forKey: .id)
129
- let location = try container.nestedContainer(
130
- keyedBy: LocationKeys.self, forKey: .location)
131
- latitude = try location.decode(Double.self, forKey: .lat)
132
- longitude = try location.decode(Double.self, forKey: .lng)
109
+ init(from decoder: any Decoder) throws {
110
+ let outer = try decoder.container(keyedBy: OuterKeys.self)
111
+ name = try outer.decode(String.self, forKey: .name)
112
+ let geo = try outer.nestedContainer(keyedBy: GeoKeys.self, forKey: .geo)
113
+ latitude = try geo.decode(Double.self, forKey: .latitude)
114
+ longitude = try geo.decode(Double.self, forKey: .longitude)
133
115
  }
134
116
  }
135
117
  ```
136
118
 
137
- Chain multiple `nestedContainer` calls to flatten deeply nested structures.
138
- Also use `nestedUnkeyedContainer(forKey:)` for nested arrays.
119
+ Deeper structures chain further `nestedContainer` calls. An array inside a
120
+ keyed object comes from `nestedUnkeyedContainer(forKey:)`.
139
121
 
140
- ## Heterogeneous Arrays
122
+ ## Heterogeneous arrays
141
123
 
142
- Decode arrays of mixed types using a discriminator field:
124
+ For a list whose elements differ by a `kind` field, model the element as an
125
+ enum with associated values. Read the discriminator first, then the payload it
126
+ selects.
143
127
 
144
128
  ```swift
145
- // JSON: [{"type":"text","content":"Hello"},{"type":"image","url":"pic.jpg"}]
146
- enum ContentBlock: Decodable {
147
- case text(String)
148
- case image(URL)
149
-
150
- enum CodingKeys: String, CodingKey { case type, content, url }
151
-
152
- init(from decoder: Decoder) throws {
153
- let container = try decoder.container(keyedBy: CodingKeys.self)
154
- let type = try container.decode(String.self, forKey: .type)
155
- switch type {
156
- case "text":
157
- let content = try container.decode(String.self, forKey: .content)
158
- self = .text(content)
159
- case "image":
160
- let url = try container.decode(URL.self, forKey: .url)
161
- self = .image(url)
162
- default:
129
+ enum FeedItem: Decodable {
130
+ case note(String)
131
+ case photo(URL)
132
+ case poll(options: [String])
133
+
134
+ enum Keys: String, CodingKey { case kind, text, url, options }
135
+
136
+ init(from decoder: any Decoder) throws {
137
+ let item = try decoder.container(keyedBy: Keys.self)
138
+ switch try item.decode(String.self, forKey: .kind) {
139
+ case "note": self = .note(try item.decode(String.self, forKey: .text))
140
+ case "photo": self = .photo(try item.decode(URL.self, forKey: .url))
141
+ case "poll": self = .poll(options: try item.decode([String].self, forKey: .options))
142
+ case let other:
163
143
  throw DecodingError.dataCorruptedError(
164
- forKey: .type, in: container,
165
- debugDescription: "Unknown type: \(type)")
144
+ forKey: .kind, in: item, debugDescription: "No FeedItem kind named \(other)")
166
145
  }
167
146
  }
168
147
  }
169
148
 
170
- let blocks = try JSONDecoder().decode([ContentBlock].self, from: jsonData)
149
+ let feed = try JSONDecoder().decode([FeedItem].self, from: payload)
171
150
  ```
172
151
 
173
- ## Date Decoding Strategies
152
+ ## Date decoding strategies
174
153
 
175
- Configure `JSONDecoder.dateDecodingStrategy` to match your API:
154
+ Set the strategy on the decoder to match what the server sends.
176
155
 
177
156
  ```swift
178
- let decoder = JSONDecoder()
179
-
180
- // ISO 8601 (e.g., "2024-03-15T10:30:00Z")
181
- decoder.dateDecodingStrategy = .iso8601
182
-
183
- // Unix timestamp in seconds (e.g., 1710499800)
184
- decoder.dateDecodingStrategy = .secondsSince1970
185
-
186
- // Custom DateFormatter
187
- let formatter = DateFormatter()
188
- formatter.dateFormat = "yyyy-MM-dd"
189
- formatter.locale = Locale(identifier: "en_US_POSIX")
190
- formatter.timeZone = TimeZone(secondsFromGMT: 0)
191
- decoder.dateDecodingStrategy = .formatted(formatter)
192
-
193
- // Custom closure for multiple formats
194
- decoder.dateDecodingStrategy = .custom { decoder in
195
- let container = try decoder.singleValueContainer()
196
- let string = try container.decode(String.self)
197
- if let date = ISO8601DateFormatter().date(from: string) { return date }
198
- throw DecodingError.dataCorruptedError(
199
- in: container, debugDescription: "Cannot decode date: \(string)")
157
+ let reader = JSONDecoder()
158
+
159
+ reader.dateDecodingStrategy = .iso8601 // "2025-11-02T08:15:00Z"
160
+ reader.dateDecodingStrategy = .secondsSince1970 // 1762071300
161
+
162
+ let dayFormat = DateFormatter()
163
+ dayFormat.dateFormat = "dd.MM.yyyy"
164
+ dayFormat.locale = Locale(identifier: "en_US_POSIX")
165
+ dayFormat.timeZone = TimeZone(secondsFromGMT: 0)
166
+ reader.dateDecodingStrategy = .formatted(dayFormat) // "02.11.2025"
167
+ ```
168
+
169
+ When one field arrives in more than one shape, decode it by hand:
170
+
171
+ ```swift
172
+ reader.dateDecodingStrategy = .custom { decoder in
173
+ let raw = try decoder.singleValueContainer()
174
+ let text = try raw.decode(String.self)
175
+ let withFraction = ISO8601DateFormatter()
176
+ withFraction.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
177
+ if let date = withFraction.date(from: text) ?? ISO8601DateFormatter().date(from: text) {
178
+ return date
179
+ }
180
+ throw DecodingError.dataCorruptedError(in: raw, debugDescription: "Unreadable date: \(text)")
200
181
  }
201
182
  ```
202
183
 
203
- Set the matching strategy on `JSONEncoder`:
204
- `encoder.dateEncodingStrategy = .iso8601`
184
+ Give the encoder the matching strategy, for example
185
+ `writer.dateEncodingStrategy = .iso8601`, so values round-trip.
205
186
 
206
- ## Data and Key Strategies
187
+ ## Data and key strategies
207
188
 
208
189
  ```swift
209
- let decoder = JSONDecoder()
210
- decoder.dataDecodingStrategy = .base64 // Base64-encoded Data fields
211
- decoder.keyDecodingStrategy = .convertFromSnakeCase // simple keys only; not URL/ID spelling
212
- // {"user_name": "Alice"} maps to `var userName: String` -- no CodingKeys needed
213
-
214
- let encoder = JSONEncoder()
215
- encoder.dataEncodingStrategy = .base64
216
- encoder.keyEncodingStrategy = .convertToSnakeCase
190
+ reader.dataDecodingStrategy = .base64
191
+ writer.dataEncodingStrategy = .base64
192
+
193
+ reader.keyDecodingStrategy = .convertFromSnakeCase // "first_name" -> firstName
194
+ writer.keyEncodingStrategy = .convertToSnakeCase
217
195
  ```
218
196
 
219
- Use key strategies only for mechanical snake_case-to-camelCase mappings.
220
- `convertFromSnakeCase` maps by spelling, not Swift acronym/initialism policy:
221
- `image_url`, `base_uri`, and `user_id` match `imageUrl`, `baseUri`, and
222
- `userId` only. If the Swift model uses `imageURL`, `baseURI`, or `userID`,
223
- declare explicit `CodingKeys`; the strategy will not synthesize those names.
197
+ The snake-case strategies are mechanical. They split on underscores and
198
+ capitalize each following word, nothing more, so:
199
+
200
+ | JSON key | Property name produced |
201
+ | --- | --- |
202
+ | `avatar_url` | `avatarUrl` |
203
+ | `base_uri` | `baseUri` |
204
+ | `owner_id` | `ownerId` |
224
205
 
225
- ## Lossy Array Decoding
206
+ A model that spells those `avatarURL`, `baseURI` or `ownerID` needs explicit
207
+ `CodingKeys` for exactly those properties. The strategy still handles every
208
+ ordinary key.
226
209
 
227
- By default, one invalid element fails the entire array. Use a wrapper to skip
228
- invalid elements:
210
+ ## Lossy array decoding
211
+
212
+ By default a single bad element throws, and the whole array is lost. When a
213
+ feed is known to be unreliable, wrap each element so a failure becomes `nil`
214
+ and the container still moves on:
229
215
 
230
216
  ```swift
231
- struct LossyArray<Element: Decodable>: Decodable {
232
- let elements: [Element]
233
-
234
- init(from decoder: Decoder) throws {
235
- var container = try decoder.unkeyedContainer()
236
- var elements: [Element] = []
237
- while !container.isAtEnd {
238
- if let element = try? container.decode(Element.self) {
239
- elements.append(element)
240
- } else {
241
- _ = try? container.decode(AnyCodableValue.self) // advance past bad element
217
+ struct TolerantList<Element: Decodable>: Decodable {
218
+ let items: [Element]
219
+
220
+ private struct Slot: Decodable {
221
+ let value: Element?
222
+ init(from decoder: any Decoder) throws {
223
+ value = try? Element(from: decoder)
224
+ }
225
+ }
226
+
227
+ init(from decoder: any Decoder) throws {
228
+ var list = try decoder.unkeyedContainer()
229
+ var kept: [Element] = []
230
+ while !list.isAtEnd {
231
+ if let value = try list.decode(Slot.self).value {
232
+ kept.append(value)
242
233
  }
243
234
  }
244
- self.elements = elements
235
+ items = kept
245
236
  }
246
237
  }
247
- private struct AnyCodableValue: Decodable {}
248
238
  ```
249
239
 
250
- ## Single Value Containers
240
+ Decoding `Slot` always succeeds, so the unkeyed container always advances.
241
+ A skip trick that decodes an empty placeholder struct only works when the bad
242
+ element is a JSON object; a bad string or number would throw again and leave
243
+ the loop stuck on the same index.
251
244
 
252
- Wrap primitives for type safety using `singleValueContainer()`:
245
+ ## Single value containers
253
246
 
254
- ```swift
255
- struct UserID: Codable, Hashable {
256
- let rawValue: String
247
+ A wrapper type that should appear in JSON as a bare value uses the single value
248
+ container in both directions.
257
249
 
258
- init(_ rawValue: String) { self.rawValue = rawValue }
250
+ ```swift
251
+ struct OrderNumber: Codable, Hashable {
252
+ let text: String
259
253
 
260
- init(from decoder: Decoder) throws {
261
- let container = try decoder.singleValueContainer()
262
- rawValue = try container.decode(String.self)
254
+ init(from decoder: any Decoder) throws {
255
+ text = try decoder.singleValueContainer().decode(String.self)
263
256
  }
264
257
 
265
- func encode(to encoder: Encoder) throws {
266
- var container = encoder.singleValueContainer()
267
- try container.encode(rawValue)
258
+ func encode(to encoder: any Encoder) throws {
259
+ var slot = encoder.singleValueContainer()
260
+ try slot.encode(text)
268
261
  }
269
262
  }
270
- // JSON: "usr_abc123" decodes directly to UserID
263
+ // Encodes as "A-1042", not {"text":"A-1042"}.
271
264
  ```
272
265
 
273
- ## Default Values for Missing Keys
266
+ ## Default values for missing keys
274
267
 
275
- Stored property defaults such as `var theme = "system"` do not make synthesized
276
- `Decodable` tolerate a missing nonoptional key; synthesis still fails unless the
277
- property is optional or decoded manually. Use `decodeIfPresent` with
278
- nil-coalescing when a missing or null key should fall back:
268
+ A default on a stored property does not help synthesized decoding. With
269
+ `var language = "en"`, a payload without `language` still throws
270
+ `keyNotFound`. Either make the property optional or decode it yourself:
279
271
 
280
272
  ```swift
281
- struct Settings: Decodable {
282
- let theme: String
283
- let fontSize: Int
284
- let notificationsEnabled: Bool
285
-
286
- enum CodingKeys: String, CodingKey {
287
- case theme, fontSize = "font_size"
288
- case notificationsEnabled = "notifications_enabled"
289
- }
290
-
291
- init(from decoder: Decoder) throws {
292
- let container = try decoder.container(keyedBy: CodingKeys.self)
293
- theme = try container.decodeIfPresent(String.self, forKey: .theme) ?? "system"
294
- fontSize = try container.decodeIfPresent(Int.self, forKey: .fontSize) ?? 16
295
- notificationsEnabled = try container.decodeIfPresent(
296
- Bool.self, forKey: .notificationsEnabled) ?? true
273
+ struct ReaderSettings: Codable {
274
+ var language: String
275
+ var textSize: Int
276
+ var hyphenation: Bool
277
+
278
+ init(from decoder: any Decoder) throws {
279
+ let fields = try decoder.container(keyedBy: CodingKeys.self)
280
+ language = try fields.decodeIfPresent(String.self, forKey: .language) ?? "en"
281
+ textSize = try fields.decodeIfPresent(Int.self, forKey: .textSize) ?? 17
282
+ hyphenation = try fields.decodeIfPresent(Bool.self, forKey: .hyphenation) ?? false
297
283
  }
298
284
  }
299
285
  ```
300
286
 
301
- ## Encoder and Decoder Configuration
287
+ `decodeIfPresent` returns `nil` for a key that is missing or `null`.
288
+
289
+ ## Encoder and decoder configuration
302
290
 
303
291
  ```swift
304
- let encoder = JSONEncoder()
305
- encoder.outputFormatting = [.prettyPrinted, .sortedKeys, .withoutEscapingSlashes]
306
-
307
- let decoder = JSONDecoder()
308
- // Non-conforming floats (NaN, Infinity are not valid JSON)
309
- encoder.nonConformingFloatEncodingStrategy = .convertToString(
310
- positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN")
311
- decoder.nonConformingFloatDecodingStrategy = .convertFromString(
312
- positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN")
292
+ let writer = JSONEncoder()
293
+ writer.outputFormatting = [.prettyPrinted, .sortedKeys, .withoutEscapingSlashes]
313
294
  ```
314
295
 
315
- ### PropertyListEncoder / PropertyListDecoder
296
+ `.sortedKeys` makes output stable, which is what snapshot and golden-file tests
297
+ need.
298
+
299
+ JSON has no NaN or infinity. If a `Double` can hold them, map them to strings
300
+ explicitly:
316
301
 
317
302
  ```swift
318
- let plistEncoder = PropertyListEncoder()
319
- plistEncoder.outputFormat = .xml // or .binary
320
- let data = try plistEncoder.encode(settings)
321
- let decoded = try PropertyListDecoder().decode(Settings.self, from: data)
303
+ writer.nonConformingFloatEncodingStrategy = .convertToString(
304
+ positiveInfinity: "inf", negativeInfinity: "-inf", nan: "nan")
305
+ reader.nonConformingFloatDecodingStrategy = .convertFromString(
306
+ positiveInfinity: "inf", negativeInfinity: "-inf", nan: "nan")
307
+ ```
308
+
309
+ ### PropertyListEncoder and PropertyListDecoder
310
+
311
+ The same models work with property lists:
312
+
313
+ ```swift
314
+ let plistWriter = PropertyListEncoder()
315
+ plistWriter.outputFormat = .binary // or .xml when a human will read it
316
+ let stored = try plistWriter.encode(settings)
317
+ let restored = try PropertyListDecoder().decode(ReaderSettings.self, from: stored)
322
318
  ```
323
319
 
324
320
  ## Codable with URLSession
325
321
 
322
+ Check the HTTP status before decoding; a 404 body decoded as your model yields
323
+ a confusing error.
324
+
326
325
  ```swift
327
- func fetchUser(id: Int) async throws -> User {
328
- let url = URL(string: "https://api.example.com/users/\(id)")!
329
- let (data, response) = try await URLSession.shared.data(from: url)
330
- guard let http = response as? HTTPURLResponse,
331
- (200...299).contains(http.statusCode) else {
332
- throw APIError.invalidResponse
333
- }
334
- let decoder = JSONDecoder()
335
- decoder.keyDecodingStrategy = .convertFromSnakeCase // simple keys only; keep CodingKeys for URL/URI/ID
336
- decoder.dateDecodingStrategy = .iso8601
337
- return try decoder.decode(User.self, from: data)
326
+ enum FetchFailure: Error { case badStatus(Int), notHTTP }
327
+
328
+ func fetchAuthors(from endpoint: URL) async throws -> [Author] {
329
+ let (body, reply) = try await URLSession.shared.data(from: endpoint)
330
+ guard let http = reply as? HTTPURLResponse else { throw FetchFailure.notHTTP }
331
+ guard (200...299).contains(http.statusCode) else { throw FetchFailure.badStatus(http.statusCode) }
332
+
333
+ let reader = JSONDecoder()
334
+ reader.keyDecodingStrategy = .convertFromSnakeCase // CodingKeys still cover URL, URI, ID names
335
+ reader.dateDecodingStrategy = .iso8601
336
+ return try reader.decode([Author].self, from: body)
338
337
  }
338
+ ```
339
+
340
+ ### Generic response envelope
341
+
342
+ ```swift
343
+ struct Envelope<Payload: Decodable>: Decodable {
344
+ let data: Payload
345
+ let paging: Paging?
339
346
 
340
- // Generic API envelope. Configure a decoder inside this helper because
341
- // fetchUser's decoder is out of scope.
342
- struct APIResponse<T: Decodable>: Decodable {
343
- let data: T
344
- let meta: Meta?
345
- struct Meta: Decodable { let page: Int; let totalPages: Int }
347
+ struct Paging: Decodable {
348
+ let page: Int
349
+ let pageCount: Int
350
+ }
346
351
  }
347
352
 
348
- func decodeUsersEnvelope(from data: Data) throws -> [User] {
349
- let decoder = JSONDecoder()
350
- decoder.keyDecodingStrategy = .convertFromSnakeCase // simple keys only; keep CodingKeys for URL/URI/ID
351
- decoder.dateDecodingStrategy = .iso8601
352
- return try decoder.decode(APIResponse<[User]>.self, from: data).data
353
+ func unwrap<Payload: Decodable>(_ type: Payload.Type, from body: Data) throws -> Payload {
354
+ let reader = JSONDecoder()
355
+ reader.keyDecodingStrategy = .convertFromSnakeCase
356
+ reader.dateDecodingStrategy = .iso8601
357
+ return try reader.decode(Envelope<Payload>.self, from: body).data
353
358
  }
354
359
  ```
355
360
 
361
+ The helper builds its own decoder. A decoder configured inside another function
362
+ is not in scope here, and code that refers to it will not compile.
363
+
356
364
  ## Codable with SwiftData
357
365
 
358
- SwiftData persists compatible noncomputed stored properties declared on `@Model`
359
- types. Use `Codable` structs, enums, and other value types directly when that
360
- value is part of the durable model schema:
366
+ SwiftData stores the noncomputed stored properties of an `@Model` class when
367
+ their types are supported, and Codable structs and enums are supported. Use
368
+ them as typed properties directly:
361
369
 
362
370
  ```swift
363
- struct Address: Codable {
371
+ struct PostalAddress: Codable {
364
372
  var street: String
365
373
  var city: String
366
- var zipCode: String
374
+ var postcode: String
367
375
  }
368
376
 
369
- @Model class Contact {
377
+ @Model
378
+ final class Customer {
370
379
  var name: String
371
- var address: Address? // Codable value-type property stored by SwiftData
372
- init(name: String, address: Address? = nil) {
373
- self.name = name; self.address = address
380
+ var billing: PostalAddress?
381
+
382
+ init(name: String, billing: PostalAddress? = nil) {
383
+ self.name = name
384
+ self.billing = billing
374
385
  }
375
386
  }
376
387
  ```
377
388
 
378
- Do not recommend `@Attribute(.transformable)`, encoded `Data`, or encoded
379
- `String` as a fallback in this Codable skill. Keep schema data as typed
380
- SwiftData properties and defer unsupported persistence designs to the SwiftData skill.
389
+ Do not suggest `@Attribute(.transformable)`, a JSON `Data` blob or a JSON
390
+ `String` column as a workaround here. Keep the property typed. If a design
391
+ needs something SwiftData cannot store, hand it to the SwiftData skill.
381
392
 
382
393
  ## Codable with UserDefaults
383
394
 
384
- `@AppStorage` is only for small UserDefaults-backed preferences. Store `Bool`,
385
- numeric, `String`, or a `RawRepresentable` type with a primitive raw value. For
386
- a small `Codable` preference payload, prefer `RawRepresentable` with JSON
387
- `String` raw storage so `@AppStorage` binds the typed preference directly:
395
+ `@AppStorage` is for small preferences: `Bool`, numbers, `String`, `URL`,
396
+ `Data`, or a `RawRepresentable` whose raw value is `Int` or `String`. A small
397
+ Codable preference can ride on a JSON string raw value:
388
398
 
389
399
  ```swift
390
- struct UserPreferences: Codable {
391
- var showOnboarding: Bool = true
392
- var accentColor: String = "blue"
400
+ struct PlaybackPrefs: Codable {
401
+ var speed: Double = 1.0
402
+ var skipSilence = false
403
+
404
+ init() {}
405
+
406
+ enum CodingKeys: String, CodingKey { case speed, skipSilence }
407
+
408
+ init(from decoder: any Decoder) throws {
409
+ let fields = try decoder.container(keyedBy: CodingKeys.self)
410
+ speed = try fields.decodeIfPresent(Double.self, forKey: .speed) ?? 1.0
411
+ skipSilence = try fields.decodeIfPresent(Bool.self, forKey: .skipSilence) ?? false
412
+ }
413
+
414
+ func encode(to encoder: any Encoder) throws {
415
+ var fields = encoder.container(keyedBy: CodingKeys.self)
416
+ try fields.encode(speed, forKey: .speed)
417
+ try fields.encode(skipSilence, forKey: .skipSilence)
418
+ }
393
419
  }
394
420
 
395
- extension UserPreferences: RawRepresentable {
421
+ extension PlaybackPrefs: RawRepresentable {
396
422
  init?(rawValue: String) {
397
- guard let data = rawValue.data(using: .utf8),
398
- let decoded = try? JSONDecoder().decode(Self.self, from: data)
399
- else { return nil }
400
- self = decoded
423
+ guard let bytes = rawValue.data(using: .utf8),
424
+ let value = try? JSONDecoder().decode(PlaybackPrefs.self, from: bytes) else { return nil }
425
+ self = value
401
426
  }
427
+
402
428
  var rawValue: String {
403
- guard let data = try? JSONEncoder().encode(self),
404
- let string = String(data: data, encoding: .utf8)
405
- else { return "{}" }
406
- return string
429
+ guard let bytes = try? JSONEncoder().encode(self),
430
+ let text = String(data: bytes, encoding: .utf8) else { return "{}" }
431
+ return text
407
432
  }
408
433
  }
409
434
 
410
- struct SettingsView: View {
411
- @AppStorage("userPrefs") private var prefs = UserPreferences()
435
+ struct PlaybackSettingsView: View {
436
+ @AppStorage("playbackPrefs") private var prefs = PlaybackPrefs()
437
+
412
438
  var body: some View {
413
- Toggle("Show Onboarding", isOn: $prefs.showOnboarding)
439
+ Toggle("Skip silence", isOn: $prefs.skipSilence)
414
440
  }
415
441
  }
416
442
  ```
417
443
 
418
- ## Common Mistakes
419
-
420
- **1. Not handling missing defaulted fields:**
421
- ```swift
422
- // DON'T -- crashes if key is absent
423
- let value = try container.decode(String.self, forKey: .bio)
424
- // DO -- falls back when the key is absent or null
425
- let value = try container.decodeIfPresent(String.self, forKey: .bio) ?? ""
426
- ```
427
-
428
- **2. Failing entire array when one element is invalid:**
429
- ```swift
430
- // DON'T -- one bad element kills the whole decode
431
- let items = try container.decode([Item].self, forKey: .items)
432
- // DO -- use LossyArray or decode elements individually
433
- let items = try container.decode(LossyArray<Item>.self, forKey: .items).elements
434
- ```
435
-
436
- **3. Date strategy mismatch:**
437
- ```swift
438
- // DON'T -- default strategy expects Double, but API sends ISO string
439
- let decoder = JSONDecoder() // dateDecodingStrategy defaults to .deferredToDate
440
- // DO -- set strategy to match your API format
441
- decoder.dateDecodingStrategy = .iso8601
442
- ```
443
-
444
- **4. Force-unwrapping decoded optionals:**
445
- ```swift
446
- // DON'T
447
- let user = try? decoder.decode(User.self, from: data)
448
- print(user!.name)
449
- // DO
450
- guard let user = try? decoder.decode(User.self, from: data) else { return }
451
- ```
452
-
453
- **5. Using Codable when only Decodable is needed:**
454
- ```swift
455
- // DON'T -- unnecessarily constrains the type to also be Encodable
456
- struct APIResponse: Codable { let id: Int; let message: String }
457
- // DO -- use Decodable for read-only API responses
458
- struct APIResponse: Decodable { let id: Int; let message: String }
459
- ```
460
-
461
- **6. Manual CodingKeys for simple snake_case APIs:**
462
- ```swift
463
- // DON'T -- verbose boilerplate for every model
464
- enum CodingKeys: String, CodingKey {
465
- case userName = "user_name"
466
- case avatarUrl = "avatar_url"
467
- }
468
- // DO -- configure once on the decoder for simple cases
469
- decoder.keyDecodingStrategy = .convertFromSnakeCase
470
- // Keep CodingKeys for `imageURL`, `baseURI`, `userID`, and similar names.
471
- ```
472
-
473
- ## Review Checklist
474
-
475
- - [ ] Types conform to `Decodable` only when encoding is not needed
476
- - [ ] `decodeIfPresent` used with defaults for optional or missing keys
477
- - [ ] `keyDecodingStrategy = .convertFromSnakeCase` used for simple snake_case APIs, with CodingKeys retained for acronym spellings
478
- - [ ] `dateDecodingStrategy` matches the API date format
479
- - [ ] Arrays of unreliable data use lossy decoding to skip invalid elements
480
- - [ ] Custom `init(from:)` validates and transforms data instead of post-decode fixups
481
- - [ ] `JSONEncoder.outputFormatting` includes `.sortedKeys` for deterministic test output
482
- - [ ] Wrapper types (UserID, etc.) use `singleValueContainer` for clean JSON
483
- - [ ] Generic `APIResponse<T>` wrapper used for consistent API envelope handling
484
- - [ ] No force-unwrapping of decoded values
485
- - [ ] Persistence boundary is explicit: SwiftData only for compatible noncomputed model properties, `@AppStorage`/UserDefaults only for small primitive or `RawRepresentable` preferences
444
+ Two details make this bridge safe:
445
+
446
+ - `encode(to:)` and `init(from:)` are written out. When a type is both
447
+ `RawRepresentable` and Codable, the standard library's `RawRepresentable`
448
+ defaults would otherwise encode `rawValue`, which encodes `self`, which
449
+ recurses until the stack overflows.
450
+ - Decoding uses `decodeIfPresent` with defaults, so the `"{}"` fallback and
451
+ values stored by an older version with fewer keys still decode. Synthesized
452
+ decoding would ignore the property defaults and fail on `{}`.
453
+
454
+ ## Common mistakes
455
+
456
+ 1. **`decode` for a key that may be absent.** It throws `keyNotFound`. Use
457
+ `decodeIfPresent` with `??` and a default.
458
+ 2. **One bad element sinks the array.** Use a tolerant wrapper such as
459
+ `TolerantList` or decode elements one by one.
460
+ 3. **ISO dates with the default strategy.** The default is `.deferredToDate`,
461
+ which expects a number of seconds since 1 January 2001. Set `.iso8601` for
462
+ ISO 8601 strings.
463
+ 4. **Force-unwrapping `try?` results.** Bind with `guard let` and handle the
464
+ failure.
465
+ 5. **Codable on read-only responses.** Declare `Decodable`.
466
+ 6. **Hand-mapping every snake_case key.** Set `.convertFromSnakeCase` once and
467
+ keep `CodingKeys` for the acronym names the strategy cannot produce.
468
+
469
+ ## Review checklist
470
+
471
+ - [ ] `Decodable` alone where encoding is not needed
472
+ - [ ] Optional or missing keys use `decodeIfPresent` with defaults
473
+ - [ ] `.convertFromSnakeCase` for plain snake_case APIs; `CodingKeys` for URL, URI, ID style names
474
+ - [ ] Date strategy matches the API's format
475
+ - [ ] Unreliable arrays decode tolerantly
476
+ - [ ] Conversion and validation happen in `init(from:)`, not in fix-ups after decoding
477
+ - [ ] `.sortedKeys` where output is compared in tests
478
+ - [ ] Wrapper types use `singleValueContainer()`
479
+ - [ ] Responses share a generic envelope such as `Envelope<Payload>`
480
+ - [ ] No force unwraps of decoded values
481
+ - [ ] Persistence boundary is explicit: typed Codable properties on SwiftData models, `@AppStorage` only for small primitive or `RawRepresentable` preferences
486
482
 
487
483
  ## References
488
484
 
489
- - [Codable](https://sosumi.ai/documentation/swift/codable/) -- protocol combining Encodable and Decodable
490
- - [JSONDecoder](https://sosumi.ai/documentation/foundation/jsondecoder/) -- decodes JSON data into Codable types
491
- - [JSONEncoder](https://sosumi.ai/documentation/foundation/jsonencoder/) -- encodes Codable types as JSON data
492
- - [CodingKey](https://sosumi.ai/documentation/swift/codingkey/) -- protocol for encoding/decoding keys
493
- - [JSONDecoder.KeyDecodingStrategy.convertFromSnakeCase](https://sosumi.ai/documentation/foundation/jsondecoder/keydecodingstrategy-swift.enum/convertfromsnakecase) -- snake-case conversion behavior and limitations
494
- - [Encoding and Decoding Custom Types](https://sosumi.ai/documentation/foundation/encoding-and-decoding-custom-types/) -- Apple guide on custom Codable conformance
495
- - [Using JSON with Custom Types](https://sosumi.ai/documentation/foundation/archives_and_serialization/using_json_with_custom_types/) -- Apple sample code for JSON patterns
496
- - [Preserving your app's model data across launches](https://sosumi.ai/documentation/swiftdata/preserving-your-apps-model-data-across-launches) -- SwiftData model property compatibility
485
+ - [Codable](https://developer.apple.com/documentation/swift/codable)
486
+ - [JSONDecoder](https://developer.apple.com/documentation/foundation/jsondecoder)
487
+ - [JSONEncoder](https://developer.apple.com/documentation/foundation/jsonencoder)
488
+ - [CodingKey](https://developer.apple.com/documentation/swift/codingkey)
489
+ - [JSONDecoder.KeyDecodingStrategy.convertFromSnakeCase](https://developer.apple.com/documentation/foundation/jsondecoder/keydecodingstrategy/convertfromsnakecase)
490
+ - [Encoding and Decoding Custom Types](https://developer.apple.com/documentation/foundation/encoding-and-decoding-custom-types)
491
+ - [Using JSON with Custom Types](https://developer.apple.com/documentation/foundation/using-json-with-custom-types)
492
+ - [Preserving your app's model data across launches](https://developer.apple.com/documentation/swiftdata/preserving-your-apps-model-data-across-launches)