@mmerterden/multi-agent-pipeline 20.7.0 → 20.8.1

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 (284) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/LICENSE +0 -10
  3. package/docs/facts.json +1 -1
  4. package/manifest.json +285 -285
  5. package/package.json +3 -3
  6. package/pipeline/lib/redact.mjs +3 -2
  7. package/pipeline/scripts/_notices.mjs +1 -1
  8. package/pipeline/scripts/gen-skills-index.mjs +13 -1
  9. package/pipeline/scripts/pre-commit-check.sh +4 -0
  10. package/pipeline/skills/.skill-manifest.json +69 -69
  11. package/pipeline/skills/shared/README.md +70 -70
  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/app-clips/SKILL.md +260 -160
  16. package/pipeline/skills/shared/external/app-clips/evals/evals.json +27 -27
  17. package/pipeline/skills/shared/external/app-clips/references/data-handoff-notifications-location.md +150 -83
  18. package/pipeline/skills/shared/external/app-clips/references/routing-and-experiences.md +135 -83
  19. package/pipeline/skills/shared/external/app-clips/references/size-capabilities-and-promotion.md +143 -85
  20. package/pipeline/skills/shared/external/app-intents/SKILL.md +302 -304
  21. package/pipeline/skills/shared/external/app-intents/evals/evals.json +21 -21
  22. package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +594 -894
  23. package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +345 -277
  24. package/pipeline/skills/shared/external/app-store-optimization/evals/evals.json +27 -23
  25. package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +107 -121
  26. package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +145 -165
  27. package/pipeline/skills/shared/external/app-store-review/SKILL.md +306 -326
  28. package/pipeline/skills/shared/external/app-store-review/evals/evals.json +21 -21
  29. package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +105 -67
  30. package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +114 -101
  31. package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +335 -360
  32. package/pipeline/skills/shared/external/apple-on-device-ai/evals/evals.json +24 -27
  33. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +215 -322
  34. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +161 -256
  35. package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +277 -387
  36. package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +196 -210
  37. package/pipeline/skills/shared/external/authentication/SKILL.md +277 -381
  38. package/pipeline/skills/shared/external/authentication/evals/evals.json +25 -25
  39. package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +135 -178
  40. package/pipeline/skills/shared/external/authentication/references/passkeys.md +111 -147
  41. package/pipeline/skills/shared/external/avkit/SKILL.md +267 -364
  42. package/pipeline/skills/shared/external/avkit/evals/evals.json +26 -26
  43. package/pipeline/skills/shared/external/avkit/references/avkit-patterns.md +375 -493
  44. package/pipeline/skills/shared/external/background-processing/SKILL.md +274 -384
  45. package/pipeline/skills/shared/external/background-processing/evals/evals.json +22 -22
  46. package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +173 -321
  47. package/pipeline/skills/shared/external/callkit-voip/SKILL.md +290 -371
  48. package/pipeline/skills/shared/external/callkit-voip/evals/evals.json +24 -24
  49. package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +175 -343
  50. package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +292 -381
  51. package/pipeline/skills/shared/external/cloudkit-sync/evals/evals.json +33 -30
  52. package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +227 -355
  53. package/pipeline/skills/shared/external/contacts-framework/SKILL.md +197 -346
  54. package/pipeline/skills/shared/external/contacts-framework/evals/evals.json +19 -21
  55. package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +169 -308
  56. package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +228 -376
  57. package/pipeline/skills/shared/external/core-bluetooth/evals/evals.json +25 -22
  58. package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +257 -337
  59. package/pipeline/skills/shared/external/core-data/SKILL.md +302 -368
  60. package/pipeline/skills/shared/external/core-data/evals/evals.json +30 -27
  61. package/pipeline/skills/shared/external/core-motion/SKILL.md +235 -324
  62. package/pipeline/skills/shared/external/core-motion/evals/evals.json +31 -27
  63. package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +210 -310
  64. package/pipeline/skills/shared/external/core-nfc/SKILL.md +292 -366
  65. package/pipeline/skills/shared/external/core-nfc/evals/evals.json +22 -24
  66. package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +233 -329
  67. package/pipeline/skills/shared/external/coreml/SKILL.md +323 -367
  68. package/pipeline/skills/shared/external/coreml/evals/evals.json +24 -21
  69. package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +562 -565
  70. package/pipeline/skills/shared/external/cryptokit/SKILL.md +253 -394
  71. package/pipeline/skills/shared/external/cryptokit/evals/evals.json +20 -18
  72. package/pipeline/skills/shared/external/cryptokit/references/cryptokit-patterns.md +299 -488
  73. package/pipeline/skills/shared/external/debugging-instruments/SKILL.md +270 -323
  74. package/pipeline/skills/shared/external/debugging-instruments/evals/evals.json +27 -30
  75. package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +167 -315
  76. package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +140 -193
  77. package/pipeline/skills/shared/external/device-integrity/SKILL.md +236 -353
  78. package/pipeline/skills/shared/external/device-integrity/evals/evals.json +25 -21
  79. package/pipeline/skills/shared/external/device-integrity/references/device-integrity-patterns.md +159 -197
  80. package/pipeline/skills/shared/external/energykit/SKILL.md +225 -392
  81. package/pipeline/skills/shared/external/energykit/evals/evals.json +29 -28
  82. package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +174 -470
  83. package/pipeline/skills/shared/external/eventkit-calendar/SKILL.md +261 -383
  84. package/pipeline/skills/shared/external/eventkit-calendar/evals/evals.json +25 -22
  85. package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +165 -268
  86. package/pipeline/skills/shared/external/healthkit/SKILL.md +252 -303
  87. package/pipeline/skills/shared/external/healthkit/evals/evals.json +24 -23
  88. package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +369 -523
  89. package/pipeline/skills/shared/external/homekit-matter/SKILL.md +233 -348
  90. package/pipeline/skills/shared/external/homekit-matter/evals/evals.json +27 -22
  91. package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +199 -305
  92. package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +368 -340
  93. package/pipeline/skills/shared/external/ios-accessibility/evals/evals.json +28 -27
  94. package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +314 -260
  95. package/pipeline/skills/shared/external/ios-accessibility/references/media-accessibility.md +97 -67
  96. package/pipeline/skills/shared/external/ios-accessibility/references/nutrition-labels.md +165 -101
  97. package/pipeline/skills/shared/external/ios-localization/SKILL.md +258 -371
  98. package/pipeline/skills/shared/external/ios-localization/evals/evals.json +23 -23
  99. package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +283 -491
  100. package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +313 -440
  101. package/pipeline/skills/shared/external/ios-networking/SKILL.md +265 -341
  102. package/pipeline/skills/shared/external/ios-networking/evals/evals.json +24 -24
  103. package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +425 -652
  104. package/pipeline/skills/shared/external/ios-networking/references/file-storage-patterns.md +143 -285
  105. package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +93 -53
  106. package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +231 -456
  107. package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +517 -784
  108. package/pipeline/skills/shared/external/ios-simulator/SKILL.md +265 -393
  109. package/pipeline/skills/shared/external/ios-simulator/evals/evals.json +21 -21
  110. package/pipeline/skills/shared/external/ios-simulator/references/simctl-commands.md +177 -270
  111. package/pipeline/skills/shared/external/live-activities/SKILL.md +318 -360
  112. package/pipeline/skills/shared/external/live-activities/evals/evals.json +21 -21
  113. package/pipeline/skills/shared/external/live-activities/references/activitykit-patterns.md +478 -710
  114. package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +3 -3
  115. package/pipeline/skills/shared/external/localization-reuse-map/example-mapping.json +1 -1
  116. package/pipeline/skills/shared/external/localization-reuse-map/reference/format-and-output.md +8 -7
  117. package/pipeline/skills/shared/external/localization-reuse-map/reference/publish-and-snapshot.md +2 -2
  118. package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md +5 -2
  119. package/pipeline/skills/shared/external/localization-reuse-map/scripts/_shared.py +100 -0
  120. package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-artifact.py +45 -26
  121. package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-spreadsheet.py +14 -16
  122. package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-annotations.py +12 -5
  123. package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-legacy-labels.py +2 -1
  124. package/pipeline/skills/shared/external/localization-reuse-map/scripts/publish-confluence.py +6 -5
  125. package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-overlay.py +44 -18
  126. package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-legacy-values.py +5 -2
  127. package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-new-values.py +10 -11
  128. package/pipeline/skills/shared/external/localization-reuse-map/scripts/scan-screen-keys.py +4 -33
  129. package/pipeline/skills/shared/external/localization-reuse-map/scripts/verify-map.py +12 -59
  130. package/pipeline/skills/shared/external/mapkit-location/SKILL.md +297 -267
  131. package/pipeline/skills/shared/external/mapkit-location/evals/evals.json +28 -24
  132. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-corelocation-patterns.md +378 -532
  133. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +397 -499
  134. package/pipeline/skills/shared/external/metrickit-diagnostics/SKILL.md +165 -348
  135. package/pipeline/skills/shared/external/metrickit-diagnostics/evals/evals.json +26 -23
  136. package/pipeline/skills/shared/external/metrickit-diagnostics/references/metrickit-patterns.md +123 -130
  137. package/pipeline/skills/shared/external/musickit-audio/SKILL.md +189 -315
  138. package/pipeline/skills/shared/external/musickit-audio/evals/evals.json +22 -21
  139. package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +181 -270
  140. package/pipeline/skills/shared/external/natural-language/SKILL.md +188 -340
  141. package/pipeline/skills/shared/external/natural-language/evals/evals.json +21 -21
  142. package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +171 -225
  143. package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +258 -392
  144. package/pipeline/skills/shared/external/passkit-wallet/evals/evals.json +30 -29
  145. package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +164 -231
  146. package/pipeline/skills/shared/external/pdfkit/SKILL.md +312 -344
  147. package/pipeline/skills/shared/external/pdfkit/evals/evals.json +19 -19
  148. package/pipeline/skills/shared/external/pdfkit/references/pdfkit-patterns.md +413 -624
  149. package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +242 -358
  150. package/pipeline/skills/shared/external/pencilkit-drawing/evals/evals.json +25 -21
  151. package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +161 -226
  152. package/pipeline/skills/shared/external/permissionkit/SKILL.md +282 -400
  153. package/pipeline/skills/shared/external/permissionkit/evals/evals.json +27 -30
  154. package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +237 -350
  155. package/pipeline/skills/shared/external/photos-camera-media/SKILL.md +276 -325
  156. package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +299 -545
  157. package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +344 -588
  158. package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +316 -660
  159. package/pipeline/skills/shared/external/photos-camera-media/references/photokit-patterns.md +270 -416
  160. package/pipeline/skills/shared/external/push-notifications/SKILL.md +312 -340
  161. package/pipeline/skills/shared/external/push-notifications/evals/evals.json +27 -26
  162. package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +328 -485
  163. package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +327 -560
  164. package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +218 -410
  165. package/pipeline/skills/shared/external/realitykit-ar/evals/evals.json +24 -27
  166. package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +221 -348
  167. package/pipeline/skills/shared/external/shareplay-activities/SKILL.md +222 -393
  168. package/pipeline/skills/shared/external/shareplay-activities/evals/evals.json +23 -24
  169. package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +280 -420
  170. package/pipeline/skills/shared/external/skill-creator/template.md +7 -1
  171. package/pipeline/skills/shared/external/speech-recognition/SKILL.md +217 -421
  172. package/pipeline/skills/shared/external/speech-recognition/evals/evals.json +23 -26
  173. package/pipeline/skills/shared/external/speech-recognition/references/speechanalyzer-patterns.md +133 -125
  174. package/pipeline/skills/shared/external/storekit/SKILL.md +228 -204
  175. package/pipeline/skills/shared/external/storekit/evals/evals.json +27 -24
  176. package/pipeline/skills/shared/external/storekit/references/app-review-guidelines.md +98 -109
  177. package/pipeline/skills/shared/external/storekit/references/core-patterns.md +302 -241
  178. package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +356 -649
  179. package/pipeline/skills/shared/external/swift-api-design-guidelines/SKILL.md +274 -399
  180. package/pipeline/skills/shared/external/swift-api-design-guidelines/evals/evals.json +22 -24
  181. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/argument-labels-and-parameters.md +107 -108
  182. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md +93 -165
  183. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md +99 -137
  184. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md +77 -120
  185. package/pipeline/skills/shared/external/swift-architecture/SKILL.md +334 -350
  186. package/pipeline/skills/shared/external/swift-architecture/evals/evals.json +22 -22
  187. package/pipeline/skills/shared/external/swift-charts/SKILL.md +208 -394
  188. package/pipeline/skills/shared/external/swift-charts/evals/evals.json +27 -30
  189. package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +351 -762
  190. package/pipeline/skills/shared/external/swift-codable/SKILL.md +339 -343
  191. package/pipeline/skills/shared/external/swift-codable/evals/evals.json +20 -20
  192. package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +304 -351
  193. package/pipeline/skills/shared/external/swift-concurrency/evals/evals.json +27 -24
  194. package/pipeline/skills/shared/external/swift-concurrency/references/approachable-concurrency.md +65 -80
  195. package/pipeline/skills/shared/external/swift-concurrency/references/async-algorithms.md +48 -84
  196. package/pipeline/skills/shared/external/swift-concurrency/references/bridging-interop.md +134 -79
  197. package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +145 -167
  198. package/pipeline/skills/shared/external/swift-concurrency/references/diagnostics.md +62 -50
  199. package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +92 -121
  200. package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +177 -241
  201. package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +258 -234
  202. package/pipeline/skills/shared/external/swift-language/SKILL.md +342 -382
  203. package/pipeline/skills/shared/external/swift-language/evals/evals.json +24 -27
  204. package/pipeline/skills/shared/external/swift-language/references/swift-attributes-interop.md +79 -56
  205. package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +297 -340
  206. package/pipeline/skills/shared/external/swift-security/SKILL.md +183 -162
  207. package/pipeline/skills/shared/external/swift-security/evals/evals.json +25 -25
  208. package/pipeline/skills/shared/external/swift-security/references/biometric-authentication.md +314 -469
  209. package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +411 -476
  210. package/pipeline/skills/shared/external/swift-security/references/common-anti-patterns.md +260 -530
  211. package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md +270 -477
  212. package/pipeline/skills/shared/external/swift-security/references/credential-storage-patterns.md +573 -571
  213. package/pipeline/skills/shared/external/swift-security/references/cryptokit-public-key.md +370 -441
  214. package/pipeline/skills/shared/external/swift-security/references/cryptokit-symmetric.md +332 -433
  215. package/pipeline/skills/shared/external/swift-security/references/keychain-access-control.md +346 -468
  216. package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +375 -491
  217. package/pipeline/skills/shared/external/swift-security/references/keychain-item-classes.md +431 -432
  218. package/pipeline/skills/shared/external/swift-security/references/keychain-sharing.md +328 -425
  219. package/pipeline/skills/shared/external/swift-security/references/migration-legacy-stores.md +341 -579
  220. package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +397 -457
  221. package/pipeline/skills/shared/external/swift-security/references/testing-security-code.md +354 -614
  222. package/pipeline/skills/shared/external/swift-testing/SKILL.md +191 -175
  223. package/pipeline/skills/shared/external/swift-testing/evals/evals.json +26 -24
  224. package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +81 -84
  225. package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +317 -433
  226. package/pipeline/skills/shared/external/swiftdata/SKILL.md +394 -256
  227. package/pipeline/skills/shared/external/swiftdata/evals/evals.json +24 -24
  228. package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +206 -402
  229. package/pipeline/skills/shared/external/swiftdata/references/indexing.md +59 -52
  230. package/pipeline/skills/shared/external/swiftdata/references/predicate-pitfalls.md +57 -33
  231. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +354 -747
  232. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +300 -508
  233. package/pipeline/skills/shared/external/swiftlint/SKILL.md +175 -226
  234. package/pipeline/skills/shared/external/swiftlint/references/adoption-and-configuration.md +141 -208
  235. package/pipeline/skills/shared/external/swiftlint/references/custom-rules-and-analyze.md +100 -109
  236. package/pipeline/skills/shared/external/swiftlint/references/plugins-run-scripts-and-integrations.md +159 -179
  237. package/pipeline/skills/shared/external/swiftlint/references/rule-reference.md +383 -18
  238. package/pipeline/skills/shared/external/swiftlint/references/rules-suppressions-and-baselines.md +143 -229
  239. package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +283 -366
  240. package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +396 -608
  241. package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +336 -385
  242. package/pipeline/skills/shared/external/swiftui-gestures/SKILL.md +239 -349
  243. package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +228 -310
  244. package/pipeline/skills/shared/external/swiftui-layout-components/SKILL.md +260 -249
  245. package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +92 -74
  246. package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +112 -177
  247. package/pipeline/skills/shared/external/swiftui-layout-components/references/list.md +61 -64
  248. package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +94 -134
  249. package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +193 -225
  250. package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +173 -327
  251. package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +201 -168
  252. package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +127 -150
  253. package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +134 -133
  254. package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +152 -117
  255. package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +111 -138
  256. package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +316 -252
  257. package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +341 -332
  258. package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +547 -854
  259. package/pipeline/skills/shared/external/swiftui-patterns/references/design-polish.md +485 -537
  260. package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +417 -499
  261. package/pipeline/skills/shared/external/swiftui-performance/SKILL.md +213 -376
  262. package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +86 -175
  263. package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +89 -195
  264. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +95 -182
  265. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-improving-swiftui-performance.md +71 -149
  266. package/pipeline/skills/shared/external/swiftui-performance/references/wwdc-session-sources.md +21 -27
  267. package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +303 -295
  268. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +204 -387
  269. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +469 -683
  270. package/pipeline/skills/shared/external/swiftui-webkit/SKILL.md +140 -186
  271. package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +75 -86
  272. package/pipeline/skills/shared/external/swiftui-webkit/references/local-content-and-custom-schemes.md +63 -60
  273. package/pipeline/skills/shared/external/swiftui-webkit/references/migration-and-fallbacks.md +69 -137
  274. package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +95 -67
  275. package/pipeline/skills/shared/external/tipkit/SKILL.md +220 -335
  276. package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +356 -494
  277. package/pipeline/skills/shared/external/vision-framework/SKILL.md +260 -375
  278. package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +393 -515
  279. package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +363 -539
  280. package/pipeline/skills/shared/external/weatherkit/SKILL.md +160 -315
  281. package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +288 -407
  282. package/pipeline/skills/shared/external/widgetkit/SKILL.md +224 -288
  283. package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +416 -719
  284. package/pipeline/skills/shared/external/NOTICE-swift-ios-skills.md +0 -39
@@ -1,46 +1,35 @@
1
1
  ---
2
2
  name: authentication
3
- description: "Implement iOS authentication flows with AuthenticationServices and LocalAuthentication. Use when building Sign in with Apple, passkey/WebAuthn registration or sign-in with ASAuthorizationPlatformPublicKeyCredentialProvider, ASAuthorizationController credential state and revocation handling, ASWebAuthenticationSession OAuth or third-party login, Password AutoFill, identity-token server validation, or local biometric re-authentication with LAContext."
3
+ description: "iOS sign-in with AuthenticationServices and LocalAuthentication: Sign in with Apple, passkey (WebAuthn) sign-up and login via ASAuthorizationPlatformPublicKeyCredentialProvider, ASAuthorizationController credential state and revocation, ASWebAuthenticationSession OAuth, third-party login, Password AutoFill, server identity-token validation, LAContext biometric re-auth prompts and policy. Use when adding or reviewing account flows."
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
  # Authentication
9
9
 
10
- Implement authentication flows on iOS using the AuthenticationServices
11
- framework, including Sign in with Apple, passkeys, OAuth/third-party web
12
- auth, Password AutoFill, and biometric re-authentication.
13
-
14
- ## Contents
15
-
16
- - [Sign in with Apple](#sign-in-with-apple)
17
- - [Credential Handling](#credential-handling)
18
- - [Credential State Checking](#credential-state-checking)
19
- - [Token Validation](#token-validation)
20
- - [Existing Account Setup Flows](#existing-account-setup-flows)
21
- - [Passkeys](#passkeys)
22
- - [ASWebAuthenticationSession (OAuth)](#aswebauthenticationsession-oauth)
23
- - [Password AutoFill Credentials](#password-autofill-credentials)
24
- - [Biometric Authentication](#biometric-authentication)
25
- - [Security Boundaries](#security-boundaries)
26
- - [SwiftUI SignInWithAppleButton](#swiftui-signinwithapplebutton)
27
- - [Common Mistakes](#common-mistakes)
28
- - [Review Checklist](#review-checklist)
29
- - [References](#references)
10
+ This skill owns the account-facing side of identity on iOS: Sign in with
11
+ Apple, passkeys, Password AutoFill, `ASAuthorizationController`, presenting an
12
+ OAuth session with `ASWebAuthenticationSession`, credential state, and local
13
+ biometric re-authentication. Code samples assume iOS 26 with Swift 6.3.
14
+
15
+ Deeper material:
16
+
17
+ - [Passkeys](references/passkeys.md) - prerequisites, registration, sign-in, AutoFill, security keys, failure modes
18
+ - [Keychain and biometrics](references/keychain-biometric.md) - token storage, `LAContext`, access-controlled items
30
19
 
31
20
  ## Sign in with Apple
32
21
 
33
- Add the "Sign in with Apple" capability in Xcode before using these APIs.
22
+ Before any code, add the Apple sign-in capability to the app target in Signing & Capabilities.
34
23
 
35
- ### UIKit: ASAuthorizationController Setup
24
+ ### Starting the request (UIKit)
36
25
 
37
26
  ```swift
38
27
  import AuthenticationServices
28
+ import UIKit
39
29
 
40
- final class LoginViewController: UIViewController {
41
- func startSignInWithApple() {
42
- let provider = ASAuthorizationAppleIDProvider()
43
- let request = provider.createRequest()
30
+ final class WelcomeViewController: UIViewController {
31
+ func beginAppleSignIn() {
32
+ let request = ASAuthorizationAppleIDProvider().createRequest()
44
33
  request.requestedScopes = [.fullName, .email]
45
34
 
46
35
  let controller = ASAuthorizationController(authorizationRequests: [request])
@@ -50,445 +39,352 @@ final class LoginViewController: UIViewController {
50
39
  }
51
40
  }
52
41
 
53
- extension LoginViewController: ASAuthorizationControllerPresentationContextProviding {
42
+ extension WelcomeViewController: ASAuthorizationControllerPresentationContextProviding {
54
43
  func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
55
- view.window!
44
+ guard let window = view.window else {
45
+ preconditionFailure("Start Sign in with Apple only while this screen is on screen")
46
+ }
47
+ return window // ASPresentationAnchor is UIWindow; UIWindow() is deprecated in iOS 26
56
48
  }
57
49
  }
58
50
  ```
59
51
 
60
- ### Delegate: Handling Success and Failure
52
+ ### Delegate callbacks
61
53
 
62
54
  ```swift
63
- extension LoginViewController: ASAuthorizationControllerDelegate {
64
- func authorizationController(
65
- controller: ASAuthorizationController,
66
- didCompleteWithAuthorization authorization: ASAuthorization
67
- ) {
68
- guard let credential = authorization.credential
69
- as? ASAuthorizationAppleIDCredential else { return }
70
-
71
- let userID = credential.user // Stable, unique, per-team identifier
72
- let email = credential.email // nil after first authorization
73
- let fullName = credential.fullName // nil after first authorization
74
- let identityToken = credential.identityToken // JWT for server validation
75
- let authCode = credential.authorizationCode // Short-lived code for server exchange
76
-
77
- // Save userID to Keychain for credential state checks
78
- // See references/keychain-biometric.md for Keychain patterns
79
- saveUserID(userID)
80
-
81
- // Send identityToken and authCode to your server
82
- authenticateWithServer(identityToken: identityToken, authCode: authCode)
55
+ extension WelcomeViewController: ASAuthorizationControllerDelegate {
56
+ func authorizationController(controller: ASAuthorizationController,
57
+ didCompleteWithAuthorization authorization: ASAuthorization) {
58
+ let credential = authorization.credential
59
+ guard let apple = credential as? ASAuthorizationAppleIDCredential else { return }
60
+ AccountVault.shared.rememberAppleUser(apple.user) // stable per-team ID
61
+ if let name = apple.fullName { ProfileDraft.shared.name = name } // first time only
62
+ if let mail = apple.email { ProfileDraft.shared.email = mail } // first time only
63
+ Task { try await SessionAPI.exchange(token: apple.identityToken, code: apple.authorizationCode) }
83
64
  }
84
65
 
85
- func authorizationController(
86
- controller: ASAuthorizationController,
87
- didCompleteWithError error: any Error
88
- ) {
89
- let authError = error as? ASAuthorizationError
90
- switch authError?.code {
91
- case .canceled:
92
- break // User dismissed
93
- case .failed:
94
- showError("Authorization failed")
95
- case .invalidResponse:
96
- showError("Invalid response")
97
- case .notHandled:
98
- showError("Not handled")
99
- case .notInteractive:
100
- break // Non-interactive request failed -- expected for silent checks
66
+ func authorizationController(controller: ASAuthorizationController,
67
+ didCompleteWithError error: Error) {
68
+ guard let authError = error as? ASAuthorizationError else { return }
69
+ switch authError.code {
70
+ case .canceled: break // user closed the sheet
71
+ case .notInteractive: break // silent check found nothing
72
+ case .failed, .invalidResponse, .notHandled:
73
+ showSignInProblem(authError)
101
74
  default:
102
- showError("Unknown error")
75
+ showSignInProblem(authError)
103
76
  }
104
77
  }
105
78
  }
106
79
  ```
107
80
 
108
- ## Credential Handling
109
-
110
- `ASAuthorizationAppleIDCredential` properties and their behavior:
81
+ `user` is a stable identifier scoped to your team; save it in the Keychain so
82
+ you can check credential state later. `identityToken` is a JWT for the server
83
+ and `authorizationCode` is a short-lived code the server exchanges; send both.
111
84
 
112
- | Property | Type | First Auth | Subsequent Auth |
113
- |---|---|---|---|
114
- | `user` | `String` | Always | Always |
115
- | `email` | `String?` | Provided if requested | `nil` |
116
- | `fullName` | `PersonNameComponents?` | Provided if requested | `nil` |
117
- | `identityToken` | `Data?` | JWT encoded as UTF-8 data | JWT encoded as UTF-8 data |
118
- | `authorizationCode` | `Data?` | Short-lived code | Short-lived code |
119
- | `realUserStatus` | `ASUserDetectionStatus` | Fraud-prevention signal | Do not rely on later attempts |
85
+ ### What the credential contains
120
86
 
121
- **Critical:** `email` and `fullName` are provided ONLY on the first
122
- authorization. Cache them immediately during the initial sign-up flow. If the
123
- user later deletes and re-adds the app, these values will not be returned.
87
+ | Property | Type | Availability |
88
+ |---|---|---|
89
+ | `user` | `String` | Every time |
90
+ | `email` | `String?` | First authorization only, if requested |
91
+ | `fullName` | `PersonNameComponents?` | First authorization only, if requested |
92
+ | `identityToken` | `Data?` | Every time, a UTF-8 JWT |
93
+ | `authorizationCode` | `Data?` | Every time, short-lived |
94
+ | `realUserStatus` | `ASUserDetectionStatus` | Anti-fraud hint; do not count on it after the first attempt |
124
95
 
125
- ```swift
126
- func handleCredential(_ credential: ASAuthorizationAppleIDCredential) {
127
- // Always persist the user identifier
128
- let userID = credential.user
129
-
130
- // Cache name and email IMMEDIATELY -- only available on first auth
131
- if let fullName = credential.fullName {
132
- let name = PersonNameComponentsFormatter().string(from: fullName)
133
- UserProfile.saveName(name) // Persist to your backend
134
- }
135
- if let email = credential.email {
136
- UserProfile.saveEmail(email) // Persist to your backend
137
- }
138
- }
139
- ```
96
+ Capture name and email the moment they arrive and send them to your backend.
97
+ Apple never sends them again, and deleting and reinstalling the app does not
98
+ bring them back. Format a name with
99
+ `PersonNameComponentsFormatter().string(from:)` before storing it.
140
100
 
141
- ## Credential State Checking
101
+ ### Credential state
142
102
 
143
- Check credential state on every app launch. The user may revoke access at
144
- any time via Settings > Apple Account > Sign-In & Security.
103
+ People can revoke the app at any time under Settings > Apple Account >
104
+ Sign-In & Security, so check on every launch:
145
105
 
146
106
  ```swift
147
- func checkCredentialState() {
107
+ @MainActor
108
+ func verifyAppleCredential(for userID: String) async {
148
109
  let provider = ASAuthorizationAppleIDProvider()
149
- guard let userID = loadSavedUserID() else {
150
- showLoginScreen()
110
+ let credentialState: ASAuthorizationAppleIDProvider.CredentialState
111
+ do {
112
+ credentialState = try await provider.credentialState(forUserID: userID)
113
+ } catch {
114
+ SessionStore.shared.presentLogin()
151
115
  return
152
116
  }
153
-
154
- provider.getCredentialState(forUserID: userID) { state, _ in
155
- DispatchQueue.main.async {
156
- switch state {
157
- case .authorized:
158
- proceedToMainApp()
159
- case .revoked:
160
- // User revoked -- sign out and clear local data
161
- signOut()
162
- showLoginScreen()
163
- case .notFound:
164
- showLoginScreen()
165
- case .transferred:
166
- // App transferred to new team -- migrate user identifier
167
- migrateUser()
168
- @unknown default:
169
- showLoginScreen()
170
- }
171
- }
117
+ switch credentialState {
118
+ case .authorized: SessionStore.shared.resume()
119
+ case .revoked: SessionStore.shared.signOutAndWipe()
120
+ case .notFound: SessionStore.shared.presentLogin()
121
+ case .transferred: SessionStore.shared.migrateTransferredUser(userID)
122
+ @unknown default: SessionStore.shared.presentLogin()
172
123
  }
173
124
  }
174
125
  ```
175
126
 
176
- ### Credential Revocation Notification
127
+ The function runs on the main actor and awaits the async form of
128
+ `getCredentialState(forUserID:completion:)`, so the session calls need no hop
129
+ back to the main queue.
177
130
 
178
- ```swift
179
- NotificationCenter.default.addObserver(
180
- forName: ASAuthorizationAppleIDProvider.credentialRevokedNotification,
181
- object: nil,
182
- queue: .main
183
- ) { _ in
184
- // Sign out immediately
185
- AuthManager.shared.signOut()
186
- }
187
- ```
131
+ `.transferred` means the app moved to a different developer team and the user
132
+ identifier must be migrated. Also subscribe to
133
+ `ASAuthorizationAppleIDProvider.credentialRevokedNotification` and sign the
134
+ user out as soon as it fires.
188
135
 
189
- ## Token Validation
136
+ ### Server validation
190
137
 
191
- The `identityToken` is a JWT. Send it to your server for validation --
192
- never trust it client-side alone.
138
+ Never accept the identity token on the device alone. Hand both values to your
139
+ backend and keep only what it returns:
193
140
 
194
141
  ```swift
195
- func sendTokenToServer(credential: ASAuthorizationAppleIDCredential) async throws {
196
- guard let tokenData = credential.identityToken,
197
- let token = String(data: tokenData, encoding: .utf8),
198
- let authCodeData = credential.authorizationCode,
199
- let authCode = String(data: authCodeData, encoding: .utf8) else {
200
- throw AuthError.missingToken
201
- }
202
-
203
- var request = URLRequest(url: URL(string: "https://api.example.com/auth/apple")!)
204
- request.httpMethod = "POST"
205
- request.setValue("application/json", forHTTPHeaderField: "Content-Type")
206
- request.httpBody = try JSONEncoder().encode(
207
- ["identityToken": token, "authorizationCode": authCode]
208
- )
209
-
210
- let (data, response) = try await URLSession.shared.data(for: request)
211
- guard (response as? HTTPURLResponse)?.statusCode == 200 else {
212
- throw AuthError.serverValidationFailed
142
+ enum SessionAPI {
143
+ struct Session: Decodable { let accessToken: String }
144
+ private static let transport = URLSession(configuration: .ephemeral)
145
+
146
+ static func exchange(token: Data?, code: Data?) async throws {
147
+ guard let token, let code,
148
+ let jwt = String(data: token, encoding: .utf8),
149
+ let grant = String(data: code, encoding: .utf8) else { throw URLError(.badServerResponse) }
150
+
151
+ guard let endpoint = URL(string: "https://api.tasknest.app/auth/apple") else { throw URLError(.badURL) }
152
+ var call = URLRequest(url: endpoint)
153
+ call.httpMethod = "POST"
154
+ call.addValue("application/json", forHTTPHeaderField: "Content-Type")
155
+ let body = try JSONEncoder().encode(["identity_token": jwt, "authorization_code": grant])
156
+ let (bytes, reply) = try await transport.upload(for: call, from: body)
157
+ let status = (reply as? HTTPURLResponse)?.statusCode ?? 0
158
+ if status != 200 { throw URLError(.userAuthenticationRequired) }
159
+
160
+ let session = try JSONDecoder().decode(Session.self, from: bytes)
161
+ try TokenVault(service: "auth").store(Data(session.accessToken.utf8), account: "access")
213
162
  }
214
- let session = try JSONDecoder().decode(SessionResponse.self, from: data)
215
- // Store session token in Keychain -- see references/keychain-biometric.md
216
- try KeychainHelper.save(session.accessToken, forKey: "accessToken")
217
163
  }
218
164
  ```
219
165
 
220
- Server-side, validate the JWT against Apple's public keys at
221
- `https://appleid.apple.com/auth/keys` (JWKS). Verify: `iss` is
222
- `https://appleid.apple.com`, `aud` matches your bundle ID, `exp` not passed.
166
+ `TokenVault` is in the [Keychain reference](references/keychain-biometric.md). The
167
+ server verifies the JWT signature against Apple's published key set (the JWKS
168
+ document served from `appleid.apple.com` under `/auth/keys`) and checks that `iss` is
169
+ `https://appleid.apple.com`, `aud` is your bundle ID, and `exp` is in the future.
223
170
 
224
- ## Existing Account Setup Flows
171
+ ### Existing accounts first
225
172
 
226
- On launch, silently check for existing Sign in with Apple and password
227
- credentials before showing a login screen:
173
+ Before showing a login screen, quietly ask the system whether the person
174
+ already has an Apple ID credential or a saved password for the app:
228
175
 
229
176
  ```swift
230
- func performExistingAccountSetupFlows() {
231
- let appleIDRequest = ASAuthorizationAppleIDProvider().createRequest()
232
- let passwordRequest = ASAuthorizationPasswordProvider().createRequest()
233
-
234
- let controller = ASAuthorizationController(
235
- authorizationRequests: [appleIDRequest, passwordRequest]
236
- )
237
- controller.delegate = self
238
- controller.presentationContextProvider = self
239
- controller.performRequests(
240
- options: .preferImmediatelyAvailableCredentials
241
- )
177
+ extension WelcomeViewController {
178
+ func offerSavedCredentials() {
179
+ let requests: [ASAuthorizationRequest] = [
180
+ ASAuthorizationAppleIDProvider().createRequest(),
181
+ ASAuthorizationPasswordProvider().createRequest(),
182
+ ]
183
+ let controller = ASAuthorizationController(authorizationRequests: requests)
184
+ controller.delegate = self
185
+ controller.presentationContextProvider = self
186
+ controller.performRequests(options: .preferImmediatelyAvailableCredentials)
187
+ }
242
188
  }
243
189
  ```
244
190
 
245
- Call this in `viewDidAppear` or on app launch. If no existing credentials
246
- are found, the delegate receives a `.notInteractive` error -- handle it
247
- silently and show your normal login UI.
248
-
249
- ## Passkeys
250
-
251
- Use passkeys for passwordless WebAuthn-style registration and sign-in. The
252
- app must have an Associated Domains entitlement for the relying party domain
253
- using the `webcredentials:` service; passkey requests fail for services the app
254
- has not configured as associated domains.
255
-
256
- For platform passkeys synced through iCloud Keychain, request a server-provided
257
- challenge and create requests with `ASAuthorizationPlatformPublicKeyCredentialProvider`:
258
-
259
- ```swift
260
- let challenge: Data = try await server.registrationChallenge()
261
- let userID: Data = try await server.passkeyUserID()
262
- let provider = ASAuthorizationPlatformPublicKeyCredentialProvider(
263
- relyingPartyIdentifier: "example.com"
264
- )
265
- let request = provider.createCredentialRegistrationRequest(
266
- challenge: challenge,
267
- name: username,
268
- userID: userID
269
- )
270
-
271
- let controller = ASAuthorizationController(authorizationRequests: [request])
272
- controller.delegate = self
273
- controller.presentationContextProvider = self
274
- controller.performRequests()
275
- ```
191
+ Run it from `viewDidAppear` or during launch. When nothing is stored, the
192
+ delegate receives `.notInteractive`; treat that silently and show the usual
193
+ login.
276
194
 
277
- For sign-in, use `createCredentialAssertionRequest(challenge:)` with a fresh
278
- server challenge, then send the resulting registration or assertion object to
279
- the relying-party server for verification:
195
+ ### SwiftUI button
280
196
 
281
197
  ```swift
282
- let request = provider.createCredentialAssertionRequest(challenge: challenge)
283
-
284
- switch authorization.credential {
285
- case let registration as ASAuthorizationPlatformPublicKeyCredentialRegistration:
286
- try await server.finishPasskeyRegistration(registration)
287
- case let assertion as ASAuthorizationPlatformPublicKeyCredentialAssertion:
288
- try await server.finishPasskeySignIn(assertion)
289
- default:
290
- break
198
+ import AuthenticationServices
199
+ import SwiftUI
200
+
201
+ struct AppleSignInRow: View {
202
+ let model: LoginModel
203
+
204
+ var body: some View {
205
+ SignInWithAppleButton(.continue) { request in
206
+ request.requestedScopes = [.fullName, .email]
207
+ } onCompletion: { outcome in
208
+ switch outcome {
209
+ case .success(let auth):
210
+ if let apple = auth.credential as? ASAuthorizationAppleIDCredential {
211
+ model.completeAppleSignIn(apple) // same server validation as UIKit
212
+ }
213
+ case .failure(let error):
214
+ model.report(error)
215
+ }
216
+ }
217
+ .signInWithAppleButtonStyle(.whiteOutline)
218
+ .frame(height: 48)
219
+ }
291
220
  }
292
221
  ```
293
222
 
294
- For inline passkey suggestions, set the username field's `textContentType` to
295
- `.username`, include the passkey assertion request in the controller, and call
296
- `performAutoFillAssistedRequests()`. Use `ASAuthorizationSecurityKeyPublicKeyCredentialProvider`
297
- only when the user must authenticate with a physical security key. See
298
- [references/passkeys.md](references/passkeys.md) for complete registration,
299
- assertion, AutoFill, and security-key patterns.
223
+ The button lives in AuthenticationServices but needs SwiftUI imported in the
224
+ same file.
300
225
 
301
- ## ASWebAuthenticationSession (OAuth)
226
+ ## Passkeys
302
227
 
303
- Use `ASWebAuthenticationSession` for OAuth and third-party authentication
304
- (Google, GitHub, etc.). Never use `WKWebView` for auth flows.
228
+ Passkeys replace passwords with WebAuthn credentials for both account creation and login. They need
229
+ the Associated Domains entitlement with a `webcredentials:` entry for the
230
+ relying-party domain; requests for a domain that is not associated fail.
231
+ Platform passkeys sync through iCloud Keychain.
305
232
 
306
- ```swift
307
- import AuthenticationServices
233
+ - Create `ASAuthorizationPlatformPublicKeyCredentialProvider(relyingPartyIdentifier:)`.
234
+ - Register with `createCredentialRegistrationRequest(challenge:name:userID:)` using a
235
+ challenge and user ID `Data` from the server.
236
+ - Sign in with `createCredentialAssertionRequest(challenge:)` and a fresh server challenge.
237
+ - Results arrive as `ASAuthorizationPlatformPublicKeyCredentialRegistration` or
238
+ `ASAuthorizationPlatformPublicKeyCredentialAssertion`; the relying-party server verifies them.
239
+ - For inline suggestions, mark the username field `.username`, include an
240
+ assertion request, and call `performAutoFillAssistedRequests()`.
241
+ - Reach for `ASAuthorizationSecurityKeyPublicKeyCredentialProvider` only when a
242
+ physical security key is required.
308
243
 
309
- final class OAuthController: NSObject, ASWebAuthenticationPresentationContextProviding {
310
- private weak var presentationAnchor: ASPresentationAnchor?
244
+ Details and code: [passkeys reference](references/passkeys.md).
311
245
 
312
- init(presentationAnchor: ASPresentationAnchor) {
313
- self.presentationAnchor = presentationAnchor
314
- }
246
+ ## OAuth with `ASWebAuthenticationSession`
247
+
248
+ Third-party logins (Google, GitHub and similar) go through
249
+ `ASWebAuthenticationSession`, never an embedded `WKWebView`.
315
250
 
316
- func startOAuthFlow() {
317
- let authURL = URL(string:
318
- "https://provider.com/oauth/authorize?client_id=YOUR_ID&redirect_uri=myapp://callback&response_type=code"
319
- )!
320
- let session = ASWebAuthenticationSession(
321
- url: authURL, callback: .customScheme("myapp")
322
- ) { callbackURL, error in
323
- guard let callbackURL, error == nil,
324
- let code = URLComponents(url: callbackURL, resolvingAgainstBaseURL: false)?
325
- .queryItems?.first(where: { $0.name == "code" })?.value else { return }
326
- Task { await self.exchangeCodeForTokens(code) }
251
+ ```swift
252
+ final class GitHubLogin: NSObject, ASWebAuthenticationPresentationContextProviding {
253
+ private var session: ASWebAuthenticationSession?
254
+
255
+ func start(authorizeURL: URL) {
256
+ let session = ASWebAuthenticationSession(url: authorizeURL,
257
+ callback: .customScheme("tasknest")) { url, error in
258
+ guard error == nil, let url,
259
+ let items = URLComponents(url: url, resolvingAgainstBaseURL: false)?.queryItems,
260
+ let code = items.first(where: { $0.name == "code" })?.value else { return }
261
+ Task { try await OAuthBackend.redeem(code: code) }
327
262
  }
328
263
  session.presentationContextProvider = self
329
- session.prefersEphemeralWebBrowserSession = true // No shared cookies
264
+ session.prefersEphemeralWebBrowserSession = true // no shared browser cookies
330
265
  session.start()
266
+ self.session = session
331
267
  }
332
268
 
333
- func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
334
- guard let presentationAnchor else {
335
- fatalError("ASWebAuthenticationSession needs the active window")
336
- }
337
- return presentationAnchor
269
+ func presentationAnchor(for webSession: ASWebAuthenticationSession) -> ASPresentationAnchor {
270
+ let scenes = UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene }
271
+ if let key = scenes.compactMap(\.keyWindow).first { return key }
272
+ guard let scene = scenes.first else { preconditionFailure("No window scene to present from") }
273
+ return ASPresentationAnchor(windowScene: scene)
338
274
  }
339
275
  }
340
276
  ```
341
277
 
342
- In SwiftUI, use `@Environment(\.webAuthenticationSession)` and call
278
+ In SwiftUI, read `@Environment(\.webAuthenticationSession)` and call
343
279
  `authenticate(using:callback:preferredBrowserSession:additionalHeaderFields:)`
344
- with `.customScheme("myapp")` or `.https(host:path:)`; prefer `.ephemeral`
345
- only when the provider flow should avoid shared browser cookies.
280
+ with `.customScheme(...)` or `.https(host:path:)`. Ask for the `.ephemeral`
281
+ browser session only when the provider's flow should not share cookies.
346
282
 
347
- ## Password AutoFill Credentials
283
+ ## Password AutoFill
348
284
 
349
- Use `ASAuthorizationPasswordProvider` to offer saved keychain credentials
350
- alongside Sign in with Apple:
285
+ `ASAuthorizationPasswordProvider` puts saved Keychain passwords in the same
286
+ controller as Sign in with Apple. The delegate then branches on the credential:
351
287
 
352
288
  ```swift
353
- func performSignIn() {
354
- let appleIDRequest = ASAuthorizationAppleIDProvider().createRequest()
355
- appleIDRequest.requestedScopes = [.fullName, .email]
356
-
357
- let passwordRequest = ASAuthorizationPasswordProvider().createRequest()
358
-
359
- let controller = ASAuthorizationController(
360
- authorizationRequests: [appleIDRequest, passwordRequest]
361
- )
362
- controller.delegate = self
363
- controller.presentationContextProvider = self
364
- controller.performRequests()
365
- }
366
-
367
- // In delegate:
368
- func authorizationController(
369
- controller: ASAuthorizationController,
370
- didCompleteWithAuthorization authorization: ASAuthorization
371
- ) {
372
- switch authorization.credential {
373
- case let appleIDCredential as ASAuthorizationAppleIDCredential:
374
- handleAppleIDLogin(appleIDCredential)
375
- case let passwordCredential as ASPasswordCredential:
376
- // User selected a saved password from keychain
377
- signInWithPassword(
378
- username: passwordCredential.user,
379
- password: passwordCredential.password
380
- )
381
- default:
382
- break
383
- }
289
+ switch authorization.credential {
290
+ case let apple as ASAuthorizationAppleIDCredential:
291
+ finishApple(apple)
292
+ case let saved as ASPasswordCredential:
293
+ signIn(username: saved.user, password: saved.password)
294
+ default:
295
+ break
384
296
  }
385
297
  ```
386
298
 
387
- Set `textContentType` on text fields for AutoFill to work:
388
-
389
- ```swift
390
- usernameField.textContentType = .username
391
- passwordField.textContentType = .password
392
- ```
299
+ AutoFill only lights up when the text fields declare `textContentType`
300
+ `.username` and `.password`.
393
301
 
394
- ## Biometric Authentication
302
+ ## Biometric re-authentication
395
303
 
396
- Use `LAContext` from LocalAuthentication for local re-authentication before
397
- showing account settings or starting sensitive actions. Do not treat a returned
398
- `Bool` as proof to unlock a stored secret; protect secrets with Keychain access
399
- control instead. See [references/keychain-biometric.md](references/keychain-biometric.md)
400
- for `SecAccessControl` and `.biometryCurrentSet` patterns.
304
+ Use `LAContext` to confirm the device owner before opening account settings or
305
+ a sensitive action. The `Bool` it returns is a UI gate, not permission to read
306
+ a secret. Put Keychain access control on anything valuable (see
307
+ [Keychain and biometrics](references/keychain-biometric.md)).
401
308
 
402
309
  ```swift
403
310
  import LocalAuthentication
404
311
 
405
- func authenticateWithBiometrics() async throws -> Bool {
312
+ func confirmOwner() async -> Bool {
406
313
  let context = LAContext()
407
- var error: NSError?
408
-
409
- guard context.canEvaluatePolicy(
410
- .deviceOwnerAuthenticationWithBiometrics, error: &error
411
- ) else {
412
- throw AuthError.biometricsUnavailable
314
+ var problem: NSError?
315
+ guard context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &problem) else {
316
+ return false
413
317
  }
414
-
415
- return try await context.evaluatePolicy(
416
- .deviceOwnerAuthenticationWithBiometrics,
417
- localizedReason: "Sign in to your account"
418
- )
318
+ let reason = "Confirm it's you to change your email"
319
+ let passed = try? await context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason)
320
+ return passed == true
419
321
  }
420
322
  ```
421
323
 
422
- **Required:** Add `NSFaceIDUsageDescription` to Info.plist. Missing this
423
- key crashes on Face ID devices.
424
-
425
- ## Security Boundaries
426
-
427
- This skill owns user-facing account authentication: Sign in with Apple,
428
- passkeys, Password AutoFill, ASAuthorizationController, OAuth session
429
- presentation, credential state, and local biometric re-authentication. Route
430
- deep security work to `swift-security`: Keychain architecture/migration,
431
- CryptoKit, Secure Enclave, certificate pinning/trust, keychain sharing, storage
432
- hardening, and OWASP MASVS/MASTG. Keep only the storage minimum here: tokens and
433
- secrets belong in Keychain; `LAContext.evaluatePolicy` alone must not release
434
- protected secrets.
435
-
436
- ## SwiftUI SignInWithAppleButton
437
-
438
- Use `SignInWithAppleButton` in SwiftUI views when the login surface is SwiftUI.
439
- Request `.fullName` and `.email`, handle `.success` and `.failure`, downcast to
440
- `ASAuthorizationAppleIDCredential`, and send the credential through the same
441
- server-validation flow as UIKit. Style with `.signInWithAppleButtonStyle(...)`.
442
-
443
- ## Common Mistakes
444
-
445
- - Assuming a saved local session means the Apple ID credential is still valid.
446
- Check credential state at launch and handle revocation notifications.
447
- - Showing a full login screen before trying existing account setup flows.
448
- Treat `.notInteractive` as the normal "no local credential" path.
449
- - Force-unwrapping `email` or `fullName`. Cache them on first authorization and
450
- handle `nil` later.
451
- - Creating an `ASAuthorizationController` without a presentation context
452
- provider. Authorization UI needs the active presentation anchor.
453
- - Storing identity tokens, authorization codes, access tokens, passwords, or
454
- passkey server state in `UserDefaults`, files, or Core Data. Store secrets in
455
- Keychain and keep relying-party passkey verification server-side.
456
- - Adding passkey requests without `webcredentials:` Associated Domains for the
457
- relying-party domain, or trying to use app-native passkeys for unrelated
458
- websites.
459
- - Expanding authentication work into CryptoKit, Secure Enclave, certificate
460
- pinning, or OWASP MASVS. Route those to `swift-security`.
461
-
462
- ## Review Checklist
463
-
464
- - [ ] "Sign in with Apple" capability added in Xcode project
324
+ Info.plist must include `NSFaceIDUsageDescription`, the same rule the
325
+ `swift-security` skill sets for biometric keychain items. The `LAContext` header
326
+ asks apps to supply it: its string is shown the first time the app uses Face ID,
327
+ and once the user denies Face ID for the app, evaluations fail with
328
+ `LAError.biometryNotAvailable`.
329
+
330
+ ## Security boundaries
331
+
332
+ This skill covers: Apple ID sign-in, passkeys, AutoFill of saved passwords, the
333
+ `ASAuthorizationController` flow, presenting OAuth web sessions, checking
334
+ credential state, and biometric re-authentication on the device.
335
+
336
+ The minimum storage rule stays here too: tokens and secrets live in the
337
+ Keychain, and `LAContext.evaluatePolicy` on its own must never unlock a
338
+ protected secret.
339
+
340
+ Hand off to `swift-security`: Keychain architecture and migration,
341
+ biometric-gated keychain items and their access-control policy, CryptoKit, Secure Enclave, certificate pinning
342
+ and trust evaluation, sharing items between apps, hardening local storage, and
343
+ mapping against OWASP MASVS/MASTG. App and device attestation belongs to `device-integrity`.
344
+
345
+ ## Common mistakes
346
+
347
+ - Assuming a cached session means Apple has not revoked the link. Check state at launch and react to the revocation notification.
348
+ - Jumping straight to the login form without first asking for saved credentials;
349
+ `.notInteractive` is simply the "nothing saved" answer.
350
+ - Force-unwrapping `email` or `fullName`.
351
+ - Building an `ASAuthorizationController` with no presentation context provider.
352
+ - Writing JWTs from Apple, one-time codes, session tokens, passwords or
353
+ passkey server records into `UserDefaults`, plain files or Core Data. Use the
354
+ Keychain, and keep passkey verification on the server.
355
+ - Passkey requests without a `webcredentials:` Associated Domains entry, or
356
+ using app passkeys for an unrelated website.
357
+ - Drifting into CryptoKit, Secure Enclave, pinning or MASVS work instead of
358
+ routing it to `swift-security`.
359
+
360
+ ## Review checklist
361
+
362
+ - [ ] Sign in with Apple capability enabled
465
363
  - [ ] `ASAuthorizationControllerPresentationContextProviding` implemented
466
- - [ ] Credential state checked on every app launch (`getCredentialState(forUserID:completion:)`)
467
- - [ ] `credentialRevokedNotification` observer registered; sign-out handled
468
- - [ ] `email` and `fullName` cached on first authorization (not assumed available later)
469
- - [ ] `identityToken` sent to server for validation, not trusted client-side only
470
- - [ ] Tokens stored in Keychain, not UserDefaults or files
471
- - [ ] `performExistingAccountSetupFlows` called before showing login UI
472
- - [ ] Error cases handled: `.canceled`, `.failed`, `.notInteractive`
473
- - [ ] `NSFaceIDUsageDescription` in Info.plist for biometric auth
474
- - [ ] `ASWebAuthenticationSession` used for OAuth (not `WKWebView`)
475
- - [ ] `prefersEphemeralWebBrowserSession` set for OAuth when appropriate
476
- - [ ] `textContentType` set on username/password fields for AutoFill
477
- - [ ] Passkey relying party has `webcredentials:` Associated Domains configured
478
- - [ ] Passkey registration/assertion challenges come from the server and are verified server-side
479
- - [ ] Deep Keychain, CryptoKit, Secure Enclave, certificate pinning, and MASVS work routed to `swift-security`
364
+ - [ ] `getCredentialState(forUserID:completion:)` runs on every launch
365
+ - [ ] `credentialRevokedNotification` observed and signs the user out
366
+ - [ ] `email` and `fullName` saved on first authorization
367
+ - [ ] `identityToken` validated by the server
368
+ - [ ] Tokens kept in the Keychain
369
+ - [ ] Existing-account flows run before the login UI
370
+ - [ ] `.canceled`, `.failed` and `.notInteractive` handled
371
+ - [ ] `NSFaceIDUsageDescription` present
372
+ - [ ] OAuth uses `ASWebAuthenticationSession`, not `WKWebView`
373
+ - [ ] `prefersEphemeralWebBrowserSession` set where cookie isolation matters
374
+ - [ ] `textContentType` set on username and password fields
375
+ - [ ] Passkey relying party listed under `webcredentials:` Associated Domains
376
+ - [ ] Passkey challenges issued and verified by the server
377
+ - [ ] Deep Keychain, CryptoKit, Secure Enclave, pinning and MASVS questions routed to `swift-security`
480
378
 
481
379
  ## References
482
380
 
483
- - Keychain & biometric patterns: [references/keychain-biometric.md](references/keychain-biometric.md)
484
- - Passkey patterns: [references/passkeys.md](references/passkeys.md)
485
- - [AuthenticationServices](https://sosumi.ai/documentation/authenticationservices)
486
- - [ASAuthorizationAppleIDProvider](https://sosumi.ai/documentation/authenticationservices/asauthorizationappleidprovider)
487
- - [ASAuthorizationAppleIDCredential](https://sosumi.ai/documentation/authenticationservices/asauthorizationappleidcredential)
488
- - [ASAuthorizationController](https://sosumi.ai/documentation/authenticationservices/asauthorizationcontroller)
489
- - [ASWebAuthenticationSession](https://sosumi.ai/documentation/authenticationservices/aswebauthenticationsession)
490
- - [Supporting passkeys](https://sosumi.ai/documentation/authenticationservices/supporting-passkeys)
491
- - [ASAuthorizationPlatformPublicKeyCredentialProvider](https://sosumi.ai/documentation/authenticationservices/asauthorizationplatformpublickeycredentialprovider)
492
- - [ASAuthorizationPasswordProvider](https://sosumi.ai/documentation/authenticationservices/asauthorizationpasswordprovider)
493
- - [SignInWithAppleButton](https://sosumi.ai/documentation/authenticationservices/signinwithapplebutton)
494
- - [Implementing User Authentication with Sign in with Apple](https://sosumi.ai/documentation/authenticationservices/implementing-user-authentication-with-sign-in-with-apple)
381
+ - [AuthenticationServices](https://developer.apple.com/documentation/authenticationservices)
382
+ - [ASAuthorizationAppleIDProvider](https://developer.apple.com/documentation/authenticationservices/asauthorizationappleidprovider)
383
+ - [ASAuthorizationAppleIDCredential](https://developer.apple.com/documentation/authenticationservices/asauthorizationappleidcredential)
384
+ - [ASAuthorizationController](https://developer.apple.com/documentation/authenticationservices/asauthorizationcontroller)
385
+ - [ASWebAuthenticationSession](https://developer.apple.com/documentation/authenticationservices/aswebauthenticationsession)
386
+ - [Supporting passkeys](https://developer.apple.com/documentation/authenticationservices/supporting-passkeys)
387
+ - [ASAuthorizationPlatformPublicKeyCredentialProvider](https://developer.apple.com/documentation/authenticationservices/asauthorizationplatformpublickeycredentialprovider)
388
+ - [ASAuthorizationPasswordProvider](https://developer.apple.com/documentation/authenticationservices/asauthorizationpasswordprovider)
389
+ - [SignInWithAppleButton](https://developer.apple.com/documentation/authenticationservices/signinwithapplebutton)
390
+ - Sample code: the AuthenticationServices sample on implementing user authentication with Apple ID sign-in