@mmerterden/multi-agent-pipeline 20.7.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 (264) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/LICENSE +0 -10
  3. package/docs/facts.json +1 -1
  4. package/manifest.json +266 -267
  5. package/package.json +2 -2
  6. package/pipeline/scripts/_notices.mjs +1 -1
  7. package/pipeline/skills/.skill-manifest.json +68 -68
  8. package/pipeline/skills/shared/README.md +70 -70
  9. package/pipeline/skills/shared/external/alarmkit/SKILL.md +373 -381
  10. package/pipeline/skills/shared/external/alarmkit/evals/evals.json +23 -18
  11. package/pipeline/skills/shared/external/alarmkit/references/alarmkit-patterns.md +328 -378
  12. package/pipeline/skills/shared/external/app-clips/SKILL.md +260 -160
  13. package/pipeline/skills/shared/external/app-clips/evals/evals.json +27 -27
  14. package/pipeline/skills/shared/external/app-clips/references/data-handoff-notifications-location.md +150 -83
  15. package/pipeline/skills/shared/external/app-clips/references/routing-and-experiences.md +135 -83
  16. package/pipeline/skills/shared/external/app-clips/references/size-capabilities-and-promotion.md +143 -85
  17. package/pipeline/skills/shared/external/app-intents/SKILL.md +302 -304
  18. package/pipeline/skills/shared/external/app-intents/evals/evals.json +21 -21
  19. package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +594 -894
  20. package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +339 -277
  21. package/pipeline/skills/shared/external/app-store-optimization/evals/evals.json +27 -23
  22. package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +105 -122
  23. package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +143 -166
  24. package/pipeline/skills/shared/external/app-store-review/SKILL.md +307 -326
  25. package/pipeline/skills/shared/external/app-store-review/evals/evals.json +21 -21
  26. package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +105 -67
  27. package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +114 -101
  28. package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +333 -360
  29. package/pipeline/skills/shared/external/apple-on-device-ai/evals/evals.json +24 -27
  30. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +215 -322
  31. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +161 -256
  32. package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +277 -387
  33. package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +196 -210
  34. package/pipeline/skills/shared/external/authentication/SKILL.md +265 -381
  35. package/pipeline/skills/shared/external/authentication/evals/evals.json +25 -25
  36. package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +133 -178
  37. package/pipeline/skills/shared/external/authentication/references/passkeys.md +111 -147
  38. package/pipeline/skills/shared/external/avkit/SKILL.md +267 -364
  39. package/pipeline/skills/shared/external/avkit/evals/evals.json +26 -26
  40. package/pipeline/skills/shared/external/avkit/references/avkit-patterns.md +375 -493
  41. package/pipeline/skills/shared/external/background-processing/SKILL.md +270 -382
  42. package/pipeline/skills/shared/external/background-processing/evals/evals.json +22 -22
  43. package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +169 -317
  44. package/pipeline/skills/shared/external/callkit-voip/SKILL.md +290 -371
  45. package/pipeline/skills/shared/external/callkit-voip/evals/evals.json +24 -24
  46. package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +175 -343
  47. package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +292 -381
  48. package/pipeline/skills/shared/external/cloudkit-sync/evals/evals.json +33 -30
  49. package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +227 -355
  50. package/pipeline/skills/shared/external/contacts-framework/SKILL.md +197 -346
  51. package/pipeline/skills/shared/external/contacts-framework/evals/evals.json +19 -21
  52. package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +169 -308
  53. package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +226 -376
  54. package/pipeline/skills/shared/external/core-bluetooth/evals/evals.json +25 -22
  55. package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +257 -337
  56. package/pipeline/skills/shared/external/core-data/SKILL.md +292 -368
  57. package/pipeline/skills/shared/external/core-data/evals/evals.json +30 -27
  58. package/pipeline/skills/shared/external/core-motion/SKILL.md +235 -324
  59. package/pipeline/skills/shared/external/core-motion/evals/evals.json +31 -27
  60. package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +210 -310
  61. package/pipeline/skills/shared/external/core-nfc/SKILL.md +292 -366
  62. package/pipeline/skills/shared/external/core-nfc/evals/evals.json +22 -24
  63. package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +233 -329
  64. package/pipeline/skills/shared/external/coreml/SKILL.md +323 -367
  65. package/pipeline/skills/shared/external/coreml/evals/evals.json +24 -21
  66. package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +562 -565
  67. package/pipeline/skills/shared/external/cryptokit/SKILL.md +253 -394
  68. package/pipeline/skills/shared/external/cryptokit/evals/evals.json +20 -18
  69. package/pipeline/skills/shared/external/cryptokit/references/cryptokit-patterns.md +299 -488
  70. package/pipeline/skills/shared/external/debugging-instruments/SKILL.md +270 -323
  71. package/pipeline/skills/shared/external/debugging-instruments/evals/evals.json +27 -30
  72. package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +167 -315
  73. package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +140 -193
  74. package/pipeline/skills/shared/external/device-integrity/SKILL.md +230 -353
  75. package/pipeline/skills/shared/external/device-integrity/evals/evals.json +25 -21
  76. package/pipeline/skills/shared/external/device-integrity/references/device-integrity-patterns.md +159 -197
  77. package/pipeline/skills/shared/external/energykit/SKILL.md +225 -392
  78. package/pipeline/skills/shared/external/energykit/evals/evals.json +29 -28
  79. package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +174 -470
  80. package/pipeline/skills/shared/external/eventkit-calendar/SKILL.md +261 -383
  81. package/pipeline/skills/shared/external/eventkit-calendar/evals/evals.json +25 -22
  82. package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +165 -268
  83. package/pipeline/skills/shared/external/healthkit/SKILL.md +252 -303
  84. package/pipeline/skills/shared/external/healthkit/evals/evals.json +24 -23
  85. package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +369 -523
  86. package/pipeline/skills/shared/external/homekit-matter/SKILL.md +233 -348
  87. package/pipeline/skills/shared/external/homekit-matter/evals/evals.json +27 -22
  88. package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +199 -305
  89. package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +368 -340
  90. package/pipeline/skills/shared/external/ios-accessibility/evals/evals.json +28 -27
  91. package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +314 -260
  92. package/pipeline/skills/shared/external/ios-accessibility/references/media-accessibility.md +97 -67
  93. package/pipeline/skills/shared/external/ios-accessibility/references/nutrition-labels.md +165 -101
  94. package/pipeline/skills/shared/external/ios-localization/SKILL.md +258 -371
  95. package/pipeline/skills/shared/external/ios-localization/evals/evals.json +23 -23
  96. package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +283 -491
  97. package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +313 -440
  98. package/pipeline/skills/shared/external/ios-networking/SKILL.md +265 -341
  99. package/pipeline/skills/shared/external/ios-networking/evals/evals.json +24 -24
  100. package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +425 -652
  101. package/pipeline/skills/shared/external/ios-networking/references/file-storage-patterns.md +143 -285
  102. package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +93 -53
  103. package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +231 -456
  104. package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +517 -784
  105. package/pipeline/skills/shared/external/ios-simulator/SKILL.md +265 -393
  106. package/pipeline/skills/shared/external/ios-simulator/evals/evals.json +21 -21
  107. package/pipeline/skills/shared/external/ios-simulator/references/simctl-commands.md +177 -270
  108. package/pipeline/skills/shared/external/live-activities/SKILL.md +318 -360
  109. package/pipeline/skills/shared/external/live-activities/evals/evals.json +21 -21
  110. package/pipeline/skills/shared/external/live-activities/references/activitykit-patterns.md +478 -710
  111. package/pipeline/skills/shared/external/mapkit-location/SKILL.md +295 -267
  112. package/pipeline/skills/shared/external/mapkit-location/evals/evals.json +28 -24
  113. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-corelocation-patterns.md +378 -532
  114. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +397 -499
  115. package/pipeline/skills/shared/external/metrickit-diagnostics/SKILL.md +165 -348
  116. package/pipeline/skills/shared/external/metrickit-diagnostics/evals/evals.json +26 -23
  117. package/pipeline/skills/shared/external/metrickit-diagnostics/references/metrickit-patterns.md +123 -130
  118. package/pipeline/skills/shared/external/musickit-audio/SKILL.md +189 -315
  119. package/pipeline/skills/shared/external/musickit-audio/evals/evals.json +22 -21
  120. package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +181 -270
  121. package/pipeline/skills/shared/external/natural-language/SKILL.md +188 -340
  122. package/pipeline/skills/shared/external/natural-language/evals/evals.json +21 -21
  123. package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +171 -225
  124. package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +258 -392
  125. package/pipeline/skills/shared/external/passkit-wallet/evals/evals.json +30 -29
  126. package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +164 -231
  127. package/pipeline/skills/shared/external/pdfkit/SKILL.md +312 -344
  128. package/pipeline/skills/shared/external/pdfkit/evals/evals.json +19 -19
  129. package/pipeline/skills/shared/external/pdfkit/references/pdfkit-patterns.md +413 -624
  130. package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +242 -358
  131. package/pipeline/skills/shared/external/pencilkit-drawing/evals/evals.json +25 -21
  132. package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +161 -226
  133. package/pipeline/skills/shared/external/permissionkit/SKILL.md +282 -400
  134. package/pipeline/skills/shared/external/permissionkit/evals/evals.json +27 -30
  135. package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +237 -350
  136. package/pipeline/skills/shared/external/photos-camera-media/SKILL.md +276 -325
  137. package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +299 -545
  138. package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +344 -588
  139. package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +316 -660
  140. package/pipeline/skills/shared/external/photos-camera-media/references/photokit-patterns.md +270 -416
  141. package/pipeline/skills/shared/external/push-notifications/SKILL.md +312 -340
  142. package/pipeline/skills/shared/external/push-notifications/evals/evals.json +27 -26
  143. package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +328 -485
  144. package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +327 -560
  145. package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +218 -410
  146. package/pipeline/skills/shared/external/realitykit-ar/evals/evals.json +24 -27
  147. package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +221 -348
  148. package/pipeline/skills/shared/external/shareplay-activities/SKILL.md +222 -393
  149. package/pipeline/skills/shared/external/shareplay-activities/evals/evals.json +23 -24
  150. package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +280 -420
  151. package/pipeline/skills/shared/external/speech-recognition/SKILL.md +217 -421
  152. package/pipeline/skills/shared/external/speech-recognition/evals/evals.json +23 -26
  153. package/pipeline/skills/shared/external/speech-recognition/references/speechanalyzer-patterns.md +133 -125
  154. package/pipeline/skills/shared/external/storekit/SKILL.md +228 -204
  155. package/pipeline/skills/shared/external/storekit/evals/evals.json +27 -24
  156. package/pipeline/skills/shared/external/storekit/references/app-review-guidelines.md +98 -109
  157. package/pipeline/skills/shared/external/storekit/references/core-patterns.md +298 -242
  158. package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +356 -649
  159. package/pipeline/skills/shared/external/swift-api-design-guidelines/SKILL.md +274 -399
  160. package/pipeline/skills/shared/external/swift-api-design-guidelines/evals/evals.json +22 -24
  161. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/argument-labels-and-parameters.md +107 -108
  162. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md +93 -165
  163. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md +99 -137
  164. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md +77 -120
  165. package/pipeline/skills/shared/external/swift-architecture/SKILL.md +334 -350
  166. package/pipeline/skills/shared/external/swift-architecture/evals/evals.json +22 -22
  167. package/pipeline/skills/shared/external/swift-charts/SKILL.md +208 -394
  168. package/pipeline/skills/shared/external/swift-charts/evals/evals.json +27 -30
  169. package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +351 -762
  170. package/pipeline/skills/shared/external/swift-codable/SKILL.md +339 -343
  171. package/pipeline/skills/shared/external/swift-codable/evals/evals.json +20 -20
  172. package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +303 -351
  173. package/pipeline/skills/shared/external/swift-concurrency/evals/evals.json +27 -24
  174. package/pipeline/skills/shared/external/swift-concurrency/references/approachable-concurrency.md +65 -80
  175. package/pipeline/skills/shared/external/swift-concurrency/references/async-algorithms.md +48 -84
  176. package/pipeline/skills/shared/external/swift-concurrency/references/bridging-interop.md +134 -79
  177. package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +145 -167
  178. package/pipeline/skills/shared/external/swift-concurrency/references/diagnostics.md +62 -50
  179. package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +92 -121
  180. package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +177 -241
  181. package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +258 -234
  182. package/pipeline/skills/shared/external/swift-language/SKILL.md +342 -382
  183. package/pipeline/skills/shared/external/swift-language/evals/evals.json +24 -27
  184. package/pipeline/skills/shared/external/swift-language/references/swift-attributes-interop.md +79 -56
  185. package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +297 -340
  186. package/pipeline/skills/shared/external/swift-security/SKILL.md +180 -161
  187. package/pipeline/skills/shared/external/swift-security/evals/evals.json +25 -25
  188. package/pipeline/skills/shared/external/swift-security/references/biometric-authentication.md +314 -469
  189. package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +408 -476
  190. package/pipeline/skills/shared/external/swift-security/references/common-anti-patterns.md +260 -530
  191. package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md +270 -477
  192. package/pipeline/skills/shared/external/swift-security/references/credential-storage-patterns.md +573 -571
  193. package/pipeline/skills/shared/external/swift-security/references/cryptokit-public-key.md +370 -441
  194. package/pipeline/skills/shared/external/swift-security/references/cryptokit-symmetric.md +332 -433
  195. package/pipeline/skills/shared/external/swift-security/references/keychain-access-control.md +346 -468
  196. package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +352 -472
  197. package/pipeline/skills/shared/external/swift-security/references/keychain-item-classes.md +431 -432
  198. package/pipeline/skills/shared/external/swift-security/references/keychain-sharing.md +328 -425
  199. package/pipeline/skills/shared/external/swift-security/references/migration-legacy-stores.md +341 -579
  200. package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +396 -457
  201. package/pipeline/skills/shared/external/swift-security/references/testing-security-code.md +354 -614
  202. package/pipeline/skills/shared/external/swift-testing/SKILL.md +188 -175
  203. package/pipeline/skills/shared/external/swift-testing/evals/evals.json +26 -24
  204. package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +80 -84
  205. package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +317 -433
  206. package/pipeline/skills/shared/external/swiftdata/SKILL.md +392 -256
  207. package/pipeline/skills/shared/external/swiftdata/evals/evals.json +24 -24
  208. package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +206 -402
  209. package/pipeline/skills/shared/external/swiftdata/references/indexing.md +59 -52
  210. package/pipeline/skills/shared/external/swiftdata/references/predicate-pitfalls.md +57 -33
  211. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +354 -747
  212. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +300 -508
  213. package/pipeline/skills/shared/external/swiftlint/SKILL.md +175 -226
  214. package/pipeline/skills/shared/external/swiftlint/references/adoption-and-configuration.md +141 -208
  215. package/pipeline/skills/shared/external/swiftlint/references/custom-rules-and-analyze.md +100 -109
  216. package/pipeline/skills/shared/external/swiftlint/references/plugins-run-scripts-and-integrations.md +159 -179
  217. package/pipeline/skills/shared/external/swiftlint/references/rule-reference.md +383 -18
  218. package/pipeline/skills/shared/external/swiftlint/references/rules-suppressions-and-baselines.md +143 -229
  219. package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +283 -366
  220. package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +396 -608
  221. package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +336 -385
  222. package/pipeline/skills/shared/external/swiftui-gestures/SKILL.md +239 -349
  223. package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +228 -310
  224. package/pipeline/skills/shared/external/swiftui-layout-components/SKILL.md +260 -249
  225. package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +92 -74
  226. package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +112 -177
  227. package/pipeline/skills/shared/external/swiftui-layout-components/references/list.md +61 -64
  228. package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +94 -134
  229. package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +193 -225
  230. package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +173 -327
  231. package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +193 -168
  232. package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +127 -150
  233. package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +132 -133
  234. package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +152 -117
  235. package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +106 -140
  236. package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +316 -252
  237. package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +341 -332
  238. package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +547 -854
  239. package/pipeline/skills/shared/external/swiftui-patterns/references/design-polish.md +485 -537
  240. package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +417 -499
  241. package/pipeline/skills/shared/external/swiftui-performance/SKILL.md +213 -376
  242. package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +86 -175
  243. package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +89 -195
  244. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +95 -182
  245. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-improving-swiftui-performance.md +71 -149
  246. package/pipeline/skills/shared/external/swiftui-performance/references/wwdc-session-sources.md +21 -27
  247. package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +303 -295
  248. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +204 -387
  249. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +469 -683
  250. package/pipeline/skills/shared/external/swiftui-webkit/SKILL.md +140 -186
  251. package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +75 -86
  252. package/pipeline/skills/shared/external/swiftui-webkit/references/local-content-and-custom-schemes.md +63 -60
  253. package/pipeline/skills/shared/external/swiftui-webkit/references/migration-and-fallbacks.md +69 -137
  254. package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +95 -67
  255. package/pipeline/skills/shared/external/tipkit/SKILL.md +220 -335
  256. package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +356 -494
  257. package/pipeline/skills/shared/external/vision-framework/SKILL.md +260 -375
  258. package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +393 -515
  259. package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +363 -539
  260. package/pipeline/skills/shared/external/weatherkit/SKILL.md +152 -310
  261. package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +288 -407
  262. package/pipeline/skills/shared/external/widgetkit/SKILL.md +216 -288
  263. package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +414 -719
  264. 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)