@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,439 +1,316 @@
1
1
  ---
2
2
  name: device-integrity
3
- description: "Verify device legitimacy and app integrity using DeviceCheck (DCDevice per-device bits) and App Attest (DCAppAttestService key generation, attestation, and assertion flows). Use when implementing fraud prevention, detecting compromised devices, validating app authenticity with Apple's servers, protecting sensitive API endpoints with attested requests, or adding device verification to a backend architecture."
3
+ description: "DeviceCheck and App Attest: DCDevice tokens and per-device bits, DCAppAttestService key generation, attestation and assertions. Use when adding fraud prevention, detecting compromised devices, validating app authenticity with Apple's servers, protecting sensitive endpoints with attested requests, or designing backend device verification. Not for sign-in, OAuth, JWT sessions, TLS or pinning."
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
  # Device Integrity
9
9
 
10
- Verify that requests to your server come from a genuine Apple device running a
11
- legitimate instance of your app. DeviceCheck provides per-device bits for
12
- simple flags (e.g., "claimed promo offer"). App Attest uses Secure Enclave keys
13
- and Apple attestation to cryptographically prove app legitimacy on sensitive
14
- requests.
10
+ Two frameworks let a backend decide whether a request really originates from
11
+ real Apple hardware running an unmodified build of your app:
15
12
 
16
- ## Contents
13
+ - **DeviceCheck** (`DCDevice`) hands your server a token it can trade with Apple
14
+ for two developer-defined bits per device. Good for simple flags such as
15
+ "this phone already redeemed the trial".
16
+ - **App Attest** (`DCAppAttestService`) creates a hardware key inside the Secure
17
+ Enclave, has Apple certify it, and then signs chosen requests with it. Good
18
+ for cryptographic proof on sensitive calls.
17
19
 
18
- - [DCDevice (DeviceCheck Tokens)](#dcdevice-devicecheck-tokens)
19
- - [DCAppAttestService (App Attest)](#dcappattestservice-app-attest)
20
- - [App Attest Key Generation](#app-attest-key-generation)
21
- - [App Attest Attestation Flow](#app-attest-attestation-flow)
22
- - [App Attest Assertion Flow](#app-attest-assertion-flow)
23
- - [Server Verification Guidance](#server-verification-guidance)
24
- - [Error Handling](#error-handling)
25
- - [Common Patterns](#common-patterns)
26
- - [Common Mistakes](#common-mistakes)
27
- - [Review Checklist](#review-checklist)
28
- - [References](#references)
20
+ Server algorithms, retries, rejected-key recovery, an integration outline and
21
+ rollout advice live in the [patterns reference](references/device-integrity-patterns.md).
22
+ The `IntegrityError` enum used below is defined there too.
29
23
 
30
- ## DCDevice (DeviceCheck Tokens)
24
+ ## DeviceCheck tokens (`DCDevice`, iOS 11+)
31
25
 
32
- [`DCDevice`](https://sosumi.ai/documentation/devicecheck/dcdevice) generates a
33
- unique, ephemeral token that identifies a device. Treat each token as
34
- single-use: generate a new token for each server operation instead of caching or
35
- reusing one. The token is sent to your server, which then communicates with
36
- Apple's servers to read or set two per-device bits. Available on iOS 11+.
37
-
38
- ### Token Generation
26
+ `DCDevice` produces an ephemeral token that identifies the device to Apple.
27
+ Each token is single-use: create a fresh one for every server operation and
28
+ never cache it. The server forwards the token to Apple to read or change the
29
+ two bits.
39
30
 
40
31
  ```swift
41
32
  import DeviceCheck
42
33
 
43
- func generateDeviceToken() async throws -> Data {
44
- guard DCDevice.current.isSupported else {
45
- throw DeviceIntegrityError.deviceCheckUnsupported
46
- }
47
-
48
- return try await DCDevice.current.generateToken()
34
+ func sendJSON(_ fields: [String: String], to url: URL) async throws -> Bool {
35
+ var req = URLRequest(url: url)
36
+ req.httpMethod = "POST"
37
+ req.addValue("application/json", forHTTPHeaderField: "Content-Type")
38
+ let payload = try JSONEncoder().encode(fields)
39
+ let (_, reply) = try await URLSession.shared.upload(for: req, from: payload)
40
+ return (reply as? HTTPURLResponse).map { $0.statusCode == 200 } ?? false
49
41
  }
50
- ```
51
-
52
- ### Sending the Token to Your Server
53
42
 
54
- ```swift
55
- func sendTokenToServer(_ token: Data) async throws {
56
- let tokenString = token.base64EncodedString()
57
-
58
- var request = URLRequest(url: serverURL.appending(path: "verify-device"))
59
- request.httpMethod = "POST"
60
- request.setValue("application/json", forHTTPHeaderField: "Content-Type")
61
- request.httpBody = try JSONEncoder().encode(["device_token": tokenString])
62
-
63
- let (_, response) = try await URLSession.shared.data(for: request)
64
- guard let httpResponse = response as? HTTPURLResponse,
65
- httpResponse.statusCode == 200 else {
66
- throw DeviceIntegrityError.serverVerificationFailed
67
- }
43
+ func claimTrial(at endpoint: URL) async throws {
44
+ guard DCDevice.current.isSupported else { throw IntegrityError.deviceCheckUnsupported }
45
+ let token = try await DCDevice.current.generateToken() // new token every call
46
+ let accepted = try await sendJSON(["device_token": token.base64EncodedString()], to: endpoint)
47
+ if !accepted { throw IntegrityError.serverVerificationFailed }
68
48
  }
69
49
  ```
70
50
 
71
- ### Server-Side Overview
72
-
73
- Your server uses the device token to call Apple's DeviceCheck API endpoints:
74
-
75
- | Endpoint | Purpose |
76
- |----------|---------|
77
- | `https://api.devicecheck.apple.com/v1/query_two_bits` | Read the two bits for a device |
78
- | `https://api.devicecheck.apple.com/v1/update_two_bits` | Set the two bits for a device |
79
- | `https://api.devicecheck.apple.com/v1/validate_device_token` | Validate a device token without reading bits |
51
+ ### What the server does
80
52
 
81
- The server authenticates with a DeviceCheck private key from the Apple Developer
82
- portal, creating a signed JWT for each request.
53
+ All three calls live under `/v1/` on the DeviceCheck host:
83
54
 
84
- Use `https://api.development.devicecheck.apple.com` only while testing; use
85
- `https://api.devicecheck.apple.com` for production.
55
+ | Path | Purpose |
56
+ |---|---|
57
+ | `query_two_bits` | Returns the current bit values |
58
+ | `update_two_bits` | Changes one or both bits |
59
+ | `validate_device_token` | Confirms a token is real, bits untouched |
86
60
 
87
- ### What the Two Bits Are For
61
+ The server signs a JWT for each call with a DeviceCheck private key created in
62
+ the Developer portal. Hosts: `api.devicecheck.apple.com` for production traffic,
63
+ and `api.development.devicecheck.apple.com` only while testing.
88
64
 
89
- Apple stores two Boolean values per device per developer team. You decide what
90
- they mean. Common uses:
65
+ ### The two bits
91
66
 
92
- - **Bit 0:** Device has claimed a promotional offer.
93
- - **Bit 1:** Device has been flagged for fraud.
67
+ Apple keeps two Booleans per device for each developer team, and you decide
68
+ what they mean. For example bit 0 might record "promo redeemed" and bit 1
69
+ "flagged for abuse". They survive deleting and reinstalling the app; only
70
+ your server can reset them through the API.
94
71
 
95
- Bits persist across app reinstall. You control when to reset them via the
96
- server API.
72
+ ## App Attest (`DCAppAttestService`, iOS 14+)
97
73
 
98
- ## DCAppAttestService (App Attest)
74
+ App Attest ties a specific app instance on a specific device to a Secure
75
+ Enclave key. It runs in three phases:
99
76
 
100
- [`DCAppAttestService`](https://sosumi.ai/documentation/devicecheck/dcappattestservice)
101
- validates that a specific instance of your app on a specific device is
102
- legitimate. It uses a hardware-backed key in the Secure Enclave to create
103
- cryptographic attestations and assertions. Available on iOS 14+.
77
+ 1. **Key generation** - the Secure Enclave makes a key pair.
78
+ 2. **Attestation** - Apple vouches that this key sits on real hardware inside
79
+ your app.
80
+ 3. **Assertion** - sign chosen requests with the attested key.
104
81
 
105
- The flow has three phases:
106
- 1. **Key generation** -- create a key pair in the Secure Enclave.
107
- 2. **Attestation** -- Apple certifies the key belongs to a genuine Apple device running your app.
108
- 3. **Assertion** -- sign server requests with the attested key to prove ongoing legitimacy.
109
-
110
- ### Checking Support
82
+ ### Support check
111
83
 
112
84
  ```swift
113
- import DeviceCheck
114
-
115
- let attestService = DCAppAttestService.shared
116
-
117
- guard attestService.isSupported else {
118
- // Fall back to DCDevice token or other risk assessment.
119
- // App Attest is not available on simulators or all device models.
120
- return
85
+ let attest = DCAppAttestService.shared
86
+ if !attest.isSupported {
87
+ // Fall back to a DCDevice token or another risk signal.
121
88
  }
122
89
  ```
123
90
 
124
- For app extensions, App Attest is supported only in Action, extensible SSO, and
125
- watchOS extensions. Treat other extension types as unsupported even if
126
- `isSupported` returns `true`.
91
+ The service is unavailable in the Simulator and on some hardware. For app
92
+ extensions only Action, extensible single sign-on and watchOS extensions are
93
+ supported; treat any other extension type as unsupported even when
94
+ `isSupported` returns true.
127
95
 
128
- ## App Attest Key Generation
96
+ ### Key generation
129
97
 
130
- Generate one cryptographic key pair per user account on each device. The
131
- private key stays in the Secure Enclave. The returned `keyId` is the only
132
- identifier your app can later use to access the key, so record and reuse the
133
- account/device-scoped `keyId`; do not share one key across users. Avoid
134
- unnecessary regeneration because each new key affects App Attest key-count risk
135
- metrics. Only treat the `keyId` as usable after your server verifies
136
- attestation. If server verification fails, discard the `keyId` and generate a
98
+ Make one key for each signed-in account on a given device. Its private half never leaves
99
+ the Secure Enclave and the returned `keyId` is your only handle to it, so store
100
+ it and reuse it for that account on that device. Never share a key between
101
+ users, and avoid regenerating: every new key moves Apple's per-device key
102
+ count, which feeds risk metrics. Treat a `keyId` as usable only once your
103
+ server accepts its attestation; if verification fails, discard it and make a
137
104
  new key before retrying.
138
105
 
139
106
  ```swift
140
107
  import DeviceCheck
108
+ import Foundation
109
+ import Security
141
110
 
142
- actor AppAttestManager {
111
+ actor AttestKeyStore {
143
112
  private let service = DCAppAttestService.shared
144
- private var keyId: String?
113
+ private let accountID: String
114
+ var keyID: String?
145
115
 
146
- /// Generate and record a key pair for App Attest.
147
- func generateKeyIfNeeded() async throws -> String {
148
- if let existingKeyId = loadKeyIdFromKeychain() {
149
- self.keyId = existingKeyId
150
- return existingKeyId
151
- }
152
-
153
- let newKeyId = try await service.generateKey()
154
- saveKeyIdToKeychain(newKeyId)
155
- self.keyId = newKeyId
156
- return newKeyId
157
- }
116
+ init(accountID: String) { self.accountID = accountID }
158
117
 
159
- // MARK: - Keychain helpers (simplified)
118
+ private var keychainAccount: String { "attest-key.\(accountID)" }
160
119
 
161
- private func saveKeyIdToKeychain(_ keyId: String) {
162
- let data = Data(keyId.utf8)
163
- let query: [String: Any] = [
120
+ var baseQuery: [String: Any] {
121
+ [
164
122
  kSecClass as String: kSecClassGenericPassword,
165
- kSecAttrAccount as String: "app-attest-key-id-\(currentAccountID)",
166
- kSecAttrService as String: Bundle.main.bundleIdentifier ?? "",
167
- kSecValueData as String: data,
168
- kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
123
+ kSecAttrService as String: Bundle.main.bundleIdentifier ?? "attest",
124
+ kSecAttrAccount as String: keychainAccount,
169
125
  ]
170
- SecItemDelete(query as CFDictionary) // Remove old if exists
171
- SecItemAdd(query as CFDictionary, nil)
172
126
  }
173
127
 
174
- private func loadKeyIdFromKeychain() -> String? {
175
- let query: [String: Any] = [
176
- kSecClass as String: kSecClassGenericPassword,
177
- kSecAttrAccount as String: "app-attest-key-id-\(currentAccountID)",
178
- kSecAttrService as String: Bundle.main.bundleIdentifier ?? "",
179
- kSecReturnData as String: true,
180
- kSecMatchLimit as String: kSecMatchLimitOne
181
- ]
182
- var result: AnyObject?
183
- let status = SecItemCopyMatching(query as CFDictionary, &result)
184
- guard status == errSecSuccess, let data = result as? Data else { return nil }
185
- return String(data: data, encoding: .utf8)
186
- }
187
- }
188
- ```
189
-
190
- **Important:** Generate the key once per user account on a device, persist that
191
- account/device `keyId`, and keep the key count low. Generating unnecessary keys
192
- pollutes App Attest risk metrics.
193
-
194
- ## App Attest Attestation Flow
195
-
196
- Attestation proves that the key was generated on a genuine Apple device running
197
- a legitimate instance of your app. You perform attestation once per key, then
198
- store the verified public key and receipt on your server. The app stores the
199
- `keyId` for future assertions after the server accepts the attestation.
200
-
201
- ### Client-Side Attestation
202
-
203
- ```swift
204
- import DeviceCheck
205
- import CryptoKit
206
-
207
- extension AppAttestManager {
208
- /// Attest the key with Apple. Send the attestation object to your server.
209
- func attestKey() async throws -> Data {
210
- guard let keyId else {
211
- throw DeviceIntegrityError.keyNotGenerated
128
+ func generateKeyIfNeeded() async throws -> String {
129
+ if let keyID { return keyID }
130
+ if let stored = loadKeyID() {
131
+ keyID = stored
132
+ return stored
212
133
  }
213
-
214
- // 1. Request a one-time challenge from your server
215
- let challenge = try await fetchServerChallenge()
216
-
217
- // 2. Hash the challenge (Apple requires a SHA-256 hash)
218
- let challengeHash = Data(SHA256.hash(data: challenge))
219
-
220
- // 3. Ask Apple to attest the key
221
- let attestation = try await service.attestKey(keyId, clientDataHash: challengeHash)
222
-
223
- // 4. Send the attestation object to your server for verification
224
- try await sendAttestationToServer(
225
- keyId: keyId,
226
- attestation: attestation,
227
- challenge: challenge
228
- )
229
-
230
- return attestation
134
+ let fresh = try await service.generateKey()
135
+ try saveKeyID(fresh)
136
+ keyID = fresh
137
+ return fresh
231
138
  }
232
139
 
233
- private func fetchServerChallenge() async throws -> Data {
234
- let url = serverURL.appending(path: "attest/challenge")
235
- let (data, _) = try await URLSession.shared.data(from: url)
236
- return data
140
+ private func saveKeyID(_ value: String) throws {
141
+ SecItemDelete(baseQuery as CFDictionary)
142
+ var item = baseQuery
143
+ item[kSecValueData as String] = Data(value.utf8)
144
+ item[kSecAttrAccessible as String] = kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
145
+ let status = SecItemAdd(item as CFDictionary, nil)
146
+ guard status == errSecSuccess else { throw IntegrityError.keyNotGenerated }
237
147
  }
238
148
 
239
- private func sendAttestationToServer(
240
- keyId: String,
241
- attestation: Data,
242
- challenge: Data
243
- ) async throws {
244
- var request = URLRequest(url: serverURL.appending(path: "attest/verify"))
245
- request.httpMethod = "POST"
246
- request.setValue("application/json", forHTTPHeaderField: "Content-Type")
247
-
248
- let payload: [String: String] = [
249
- "key_id": keyId,
250
- "attestation": attestation.base64EncodedString(),
251
- "challenge": challenge.base64EncodedString()
252
- ]
253
- request.httpBody = try JSONEncoder().encode(payload)
254
-
255
- let (_, response) = try await URLSession.shared.data(for: request)
256
- guard let httpResponse = response as? HTTPURLResponse,
257
- httpResponse.statusCode == 200 else {
258
- throw DeviceIntegrityError.attestationVerificationFailed
259
- }
149
+ private func loadKeyID() -> String? {
150
+ var query = baseQuery
151
+ query[kSecReturnData as String] = true
152
+ query[kSecMatchLimit as String] = kSecMatchLimitOne
153
+ var found: CFTypeRef?
154
+ let status = SecItemCopyMatching(query as CFDictionary, &found)
155
+ guard status == errSecSuccess, let bytes = found as? Data else { return nil }
156
+ return String(data: bytes, encoding: .utf8)
260
157
  }
261
158
  }
262
159
  ```
263
160
 
264
- ### Server-Side Attestation Verification
161
+ Save, load and delete all build on the same `baseQuery`, so the account
162
+ attribute always carries the account suffix. The delete path in the
163
+ [rejected-key section](references/device-integrity-patterns.md#rejected-keys)
164
+ reuses it for that reason.
265
165
 
266
- Your server validates the attestation object (CBOR), verifies the certificate
267
- chain against Apple's App Attest root CA, checks Apple's nonce calculation, and
268
- stores the verified public key and receipt for future assertion verification.
269
- The attestation nonce is not `SHA256(challenge)` alone; it is
270
- `SHA256(authData || SHA256(challenge))` and is compared with the credential
271
- certificate extension `1.2.840.113635.100.8.2`. See
272
- [references/device-integrity-patterns.md](references/device-integrity-patterns.md)
273
- for the full server verification flow.
166
+ ### Attestation
274
167
 
275
- ## App Attest Assertion Flow
168
+ Attestation proves the key was created on a real device inside your genuine
169
+ app. Do it once per key. The server records the public key and receipt it verified;
170
+ the app keeps using the `keyId` for assertions once the server has accepted it.
276
171
 
277
- After attestation, use assertions to sign sensitive requests. Each assertion
278
- proves the request came from the attested app instance and includes a
279
- server-issued, one-time challenge to prevent replay.
280
-
281
- ### Client-Side Assertion
172
+ Client order: ask the server for a one-time challenge, hash it with SHA-256,
173
+ call `attestKey(_:clientDataHash:)`, and upload the result.
282
174
 
283
175
  ```swift
284
- import DeviceCheck
285
176
  import CryptoKit
286
177
 
287
- extension AppAttestManager {
288
- /// Generate an assertion for encoded client data.
289
- /// Client data should include a one-time server challenge and request context.
290
- func generateAssertion(for clientData: Data) async throws -> Data {
291
- guard let keyId else {
292
- throw DeviceIntegrityError.keyNotGenerated
293
- }
294
-
295
- let clientDataHash = Data(SHA256.hash(data: clientData))
178
+ extension AttestKeyStore {
179
+ func attest(challenge: Data, uploadTo url: URL) async throws {
180
+ let keyID = try await generateKeyIfNeeded()
181
+ let hash = Data(SHA256.hash(data: challenge))
182
+ let attestation = try await DCAppAttestService.shared.attestKey(keyID, clientDataHash: hash)
296
183
 
297
- return try await service.generateAssertion(keyId, clientDataHash: clientDataHash)
184
+ let accepted = try await sendJSON([
185
+ "key_id": keyID,
186
+ "attestation": attestation.base64EncodedString(),
187
+ "challenge": challenge.base64EncodedString(),
188
+ ], to: url)
189
+ guard accepted else { throw IntegrityError.attestationVerificationFailed }
298
190
  }
299
191
  }
300
192
  ```
301
193
 
302
- ### Using Assertions in Network Requests
194
+ On the server: parse the CBOR object, walk its certificates up to Apple's
195
+ App Attest root, check the nonce, then persist the public key and receipt. The
196
+ nonce is **not** `SHA256(challenge)`. It is `SHA256(authData || SHA256(challenge))`,
197
+ and it has to match the octet string stored under OID `1.2.840.113635.100.8.2`
198
+ in the leaf certificate.
199
+
200
+ ### Assertion
201
+
202
+ An assertion signs one sensitive request and embeds a server-issued, one-time
203
+ challenge so a captured request cannot be replayed. Call
204
+ `generateAssertion(_:clientDataHash:)` with the SHA-256 of encoded client data
205
+ that holds the challenge plus request context.
303
206
 
304
207
  ```swift
305
- struct AppAttestClientData: Encodable {
208
+ struct SignedRequestContext: Codable {
306
209
  let challenge: String
307
210
  let method: String
308
211
  let path: String
309
- let bodySHA256: String
212
+ let bodyDigest: String
310
213
  }
311
214
 
312
- extension AppAttestManager {
313
- /// Perform an attested API request.
314
- func makeAttestedRequest(
315
- to url: URL,
316
- method: String = "POST",
317
- body: Data
318
- ) async throws -> (Data, URLResponse) {
319
- let challenge = try await fetchAssertionChallenge()
320
- let bodyHash = Data(SHA256.hash(data: body)).base64EncodedString()
321
- let clientData = try JSONEncoder().encode(
322
- AppAttestClientData(
323
- challenge: challenge,
324
- method: method,
325
- path: url.path,
326
- bodySHA256: bodyHash
327
- )
215
+ extension AttestKeyStore {
216
+ func signed(_ original: URLRequest, body: Data, challenge: Data) async throws -> URLRequest {
217
+ var request = original
218
+ let keyID = try await generateKeyIfNeeded()
219
+ let context = SignedRequestContext(
220
+ challenge: challenge.base64EncodedString(),
221
+ method: request.httpMethod ?? "GET",
222
+ path: request.url?.path ?? "/",
223
+ bodyDigest: SHA256.hash(data: body).map { String(format: "%02x", $0) }.joined()
328
224
  )
329
- let assertion = try await generateAssertion(for: clientData)
330
-
331
- var request = URLRequest(url: url)
332
- request.httpMethod = method
333
- request.setValue("application/json", forHTTPHeaderField: "Content-Type")
334
- request.setValue(assertion.base64EncodedString(), forHTTPHeaderField: "X-App-Attest-Assertion")
335
- request.setValue(clientData.base64EncodedString(), forHTTPHeaderField: "X-App-Attest-Client-Data")
225
+ let encoded = try JSONEncoder().encode(context)
226
+ let assertion = try await DCAppAttestService.shared.generateAssertion(
227
+ keyID, clientDataHash: Data(SHA256.hash(data: encoded))
228
+ )
229
+ let headers = [
230
+ "X-App-Attest-Assertion": assertion.base64EncodedString(),
231
+ "X-App-Attest-Client-Data": encoded.base64EncodedString(),
232
+ ]
233
+ headers.forEach { request.addValue($0.value, forHTTPHeaderField: $0.key) }
336
234
  request.httpBody = body
337
-
338
- return try await URLSession.shared.data(for: request)
339
- }
340
-
341
- private func fetchAssertionChallenge() async throws -> String {
342
- let url = serverURL.appending(path: "assert/challenge")
343
- let (data, _) = try await URLSession.shared.data(from: url)
344
- return String(decoding: data, as: UTF8.self)
235
+ return request
345
236
  }
346
237
  }
347
238
  ```
348
239
 
349
- ### Server-Side Assertion Verification
350
-
351
- Your server decodes the assertion (CBOR), verifies the authenticator data and
352
- counter, recomputes `clientDataHash` from the submitted client data, verifies
353
- the signature over `SHA256(authenticatorData || clientDataHash)` with the
354
- stored public key, and confirms the embedded challenge and request context. See
355
- [references/device-integrity-patterns.md](references/device-integrity-patterns.md)
356
- for step-by-step server verification.
357
-
358
- ## Server Verification Guidance
359
-
360
- See [references/device-integrity-patterns.md](references/device-integrity-patterns.md) for full server architecture guidance including attestation vs. assertion comparison, recommended endpoint design, and risk assessment.
361
-
362
- ### Security Boundaries
363
-
364
- App Attest proves app-instance integrity for selected requests. It does not
365
- replace user authentication, OAuth/JWT/session handling, API token design,
366
- entitlement or subscription authorization, TLS, certificate pinning, or general
367
- networking security. Treat those as handoffs to authentication, networking, or
368
- broader security guidance, and still enforce normal authentication and
369
- authorization after App Attest passes.
370
-
371
- ## Error Handling
372
-
373
- Handle `DCError` codes from DeviceCheck operations. Key cases:
374
-
375
- - `.serverUnavailable` - retry with exponential backoff
376
- - `.invalidKey` - the key was already attested, assertion used an unattested key, or the service rejected the key
377
- - `.featureUnsupported` - fall back to `DCDevice` tokens
378
- - `.invalidInput` - malformed `clientDataHash` or `keyId`
379
-
380
- For `attestKey`, retry `.serverUnavailable` later with the same `keyId` and the
381
- same `clientDataHash`. For other attestation errors, discard the key identifier
382
- and create a new key before retrying. See
383
- [references/device-integrity-patterns.md](references/device-integrity-patterns.md)
384
- for full error handling code, retry strategy, and rejected-key recovery.
385
-
386
- ## Common Patterns
387
-
388
- ### Environment Entitlement
389
-
390
- Set the App Attest environment in your entitlements file. Use `development`
391
- during testing and `production` for App Store builds:
392
-
393
- ```xml
394
- <key>com.apple.developer.devicecheck.appattest-environment</key>
395
- <string>production</string>
396
- ```
397
-
398
- When the entitlement is omitted during development, the app uses the App Attest
399
- sandbox by default. After distribution through TestFlight, the App Store, or the
400
- Apple Developer Enterprise Program, the app ignores the entitlement value and
401
- uses production.
402
-
403
- See [references/device-integrity-patterns.md](references/device-integrity-patterns.md) for the full integration manager pattern, gradual rollout guidance, and error type definition.
404
-
405
- ## Common Mistakes
406
-
407
- 1. **Generating a new key on every launch.** Generate once per user account on a device, persist the `keyId`, and keep key counts low.
408
- 2. **Reusing `DCDevice` tokens.** Treat generated tokens as single-use. Generate a new token for each server operation.
409
- 3. **Skipping the fallback for unsupported devices or extensions.** Not all devices and extension types support App Attest. Use `DCDevice` tokens or other risk assessment as fallback.
410
- 4. **Trusting attestation client-side.** All verification must happen on your server.
411
- 5. **Signing only the raw request body.** Assertion client data must include a one-time server challenge and enough request context for the server to bind the assertion to the request.
412
- 6. **Verifying the wrong attestation nonce.** Compare the certificate extension with `SHA256(authData || SHA256(challenge))`, not `SHA256(challenge)` alone.
413
- 7. **Not implementing replay protection.** The server must validate one-time challenges and track the assertion counter.
414
- 8. **Mixing development and production environments.** Sandbox keys and receipts do not work in production, and production keys and receipts do not work in sandbox.
415
- 9. **Not handling `DCError.invalidKey`.** Check for repeated attestation, unattested assertion keys, or service rejection; regenerate only after the state is known bad.
416
-
417
- ## Review Checklist
418
-
419
- - [ ] `DCDevice` tokens generated per server operation and never cached for reuse
420
- - [ ] `DCAppAttestService.isSupported` checked before use; unsupported devices and extension types have a fallback
421
- - [ ] Key generated once per user account on each device and `keyId` persisted only for that app account/device
422
- - [ ] Attestation performed once per key; server stores verified public key and receipt
423
- - [ ] Server validates attestation certificate chain, App ID hash, environment `aaguid`, credential ID, and nonce `SHA256(authData || SHA256(challenge))`
424
- - [ ] Assertions include one-time challenge plus request context; server verifies signature, RP ID, counter, challenge, and request binding
425
- - [ ] Protected endpoints still enforce normal user authentication and entitlement authorization after App Attest passes
426
- - [ ] `DCError` cases handled: `.serverUnavailable` retries attestation with the same key/hash; bad keys are discarded and regenerated
427
- - [ ] App Attest environment entitlement and sandbox/production server routing are consistent
428
- - [ ] Gradual rollout considered; feature flag in place for enabling/disabling
240
+ Fetch a new assertion challenge from your challenge endpoint for every request.
241
+
242
+ Server side: decode the CBOR assertion, inspect authenticator data and its
243
+ counter, hash the client data it received to get `clientDataHash` again, check
244
+ that the stored public key signed `SHA256(authenticatorData || clientDataHash)`,
245
+ and make sure the challenge and the request context both line up.
246
+
247
+ ## Security boundaries
248
+
249
+ App Attest says "this request came from a genuine instance of the app". It
250
+ says nothing about who the user is. Sign-in, OAuth or JWT sessions, how API
251
+ tokens are designed, paid-feature and subscription checks, TLS and certificate
252
+ pinning all remain separate jobs. Keep enforcing authentication and authorization after the
253
+ attestation check passes. Hand-offs:
254
+
255
+ - Apple ID sign-in, passkeys, OAuth flows, credential state: `authentication`
256
+ - Keychain policy, Secure Enclave, pinning, storage hardening: `swift-security`
257
+ - Hashing and signing primitives: `cryptokit`
258
+ - Transport, request pipelines, TLS configuration: `ios-networking`
259
+
260
+ ## Errors
261
+
262
+ | `DCError.Code` | Meaning | Action |
263
+ |---|---|---|
264
+ | `.serverUnavailable` | Apple's service is temporarily unreachable | Retry with exponential backoff |
265
+ | `.invalidKey` | Key already attested, assertion with an unattested key, or key rejected | Find out which, then recover |
266
+ | `.featureUnsupported` | Device or OS lacks App Attest | Use `DCDevice` tokens instead |
267
+ | `.invalidInput` | Malformed `clientDataHash` or `keyId` | Fix the caller |
268
+
269
+ When `attestKey` reports `.serverUnavailable`, try again later passing the
270
+ **same** `keyId` and the **same** `clientDataHash`. Any other attestation
271
+ failure means the key identifier is spent: drop it and make a fresh key first.
272
+
273
+ ## Environment entitlement
274
+
275
+ The App Attest environment entitlement
276
+ (`com.apple.developer.devicecheck.appattest-environment`) takes `development`
277
+ for testing or `production` for release. Without it, development builds talk
278
+ to the App Attest sandbox. TestFlight, App Store and Enterprise Program builds
279
+ ignore the value and always use production.
280
+
281
+ ## Common mistakes
282
+
283
+ - Generating a key on every launch instead of once per account per device.
284
+ - Caching or reusing a `DCDevice` token.
285
+ - No fallback path for unsupported devices or extension types.
286
+ - Trusting attestation on the client. Only the server can verify it.
287
+ - Signing just the raw body. Include the one-time challenge and request context.
288
+ - Checking the nonce as `SHA256(challenge)`.
289
+ - No replay defense. The backend has to accept each challenge once and keep the per-key counter moving forward.
290
+ - Mixing environments. Sandbox keys and receipts fail against production and the reverse.
291
+ - Regenerating on `invalidKey` without knowing why. Establish whether the key
292
+ was already attested, never attested, or rejected, and only replace it when
293
+ its state is known to be bad.
294
+
295
+ ## Review checklist
296
+
297
+ - [ ] A new `DCDevice` token per operation; none cached
298
+ - [ ] `isSupported` checked, with a fallback for devices and extension types that lack App Attest
299
+ - [ ] One key per account per device; `keyId` stored only for that account on that device
300
+ - [ ] Keychain save, load and delete all use the same account-scoped query
301
+ - [ ] One attestation per key; server keeps the public key and receipt
302
+ - [ ] Attestation verifier covers: certificates, App ID hash, `aaguid` for the environment, credential ID, and a nonce of `SHA256(authData || SHA256(challenge))`
303
+ - [ ] Each assertion signs a challenge plus request context; the backend checks the signature, RP ID, counter, the challenge and how it binds to the request
304
+ - [ ] Protected endpoints still require user authentication and authorization
305
+ - [ ] `DCError` handled: `.serverUnavailable` retried with same key and hash, bad keys discarded and regenerated
306
+ - [ ] Environment entitlement agrees with sandbox or production server routing
307
+ - [ ] Rollout gated behind a feature flag
429
308
 
430
309
  ## References
431
310
 
432
- - Extended patterns: [references/device-integrity-patterns.md](references/device-integrity-patterns.md)
433
- - [DeviceCheck framework](https://sosumi.ai/documentation/devicecheck)
434
- - [DCDevice](https://sosumi.ai/documentation/devicecheck/dcdevice)
435
- - [DCAppAttestService](https://sosumi.ai/documentation/devicecheck/dcappattestservice)
436
- - [Establishing your app's integrity](https://sosumi.ai/documentation/devicecheck/establishing-your-app-s-integrity)
437
- - [Validating apps that connect to your server](https://sosumi.ai/documentation/devicecheck/validating-apps-that-connect-to-your-server)
438
- - [Attestation Object Validation Guide](https://sosumi.ai/documentation/devicecheck/attestation-object-validation-guide)
439
- - [App Attest Environment](https://sosumi.ai/documentation/bundleresources/entitlements/com.apple.developer.devicecheck.appattest-environment)
311
+ - [Patterns reference](references/device-integrity-patterns.md) - server verification steps, architecture, retries, rejected keys, rollout
312
+ - Apple documentation: [DeviceCheck](https://developer.apple.com/documentation/devicecheck),
313
+ [DCDevice](https://developer.apple.com/documentation/devicecheck/dcdevice),
314
+ [DCAppAttestService](https://developer.apple.com/documentation/devicecheck/dcappattestservice),
315
+ plus the DeviceCheck articles on app integrity, server-side validation,
316
+ attestation objects and the environment entitlement (see the patterns reference)