@mmerterden/multi-agent-pipeline 20.7.0 → 20.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (264) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/LICENSE +0 -10
  3. package/docs/facts.json +1 -1
  4. package/manifest.json +266 -267
  5. package/package.json +2 -2
  6. package/pipeline/scripts/_notices.mjs +1 -1
  7. package/pipeline/skills/.skill-manifest.json +68 -68
  8. package/pipeline/skills/shared/README.md +70 -70
  9. package/pipeline/skills/shared/external/alarmkit/SKILL.md +373 -381
  10. package/pipeline/skills/shared/external/alarmkit/evals/evals.json +23 -18
  11. package/pipeline/skills/shared/external/alarmkit/references/alarmkit-patterns.md +328 -378
  12. package/pipeline/skills/shared/external/app-clips/SKILL.md +260 -160
  13. package/pipeline/skills/shared/external/app-clips/evals/evals.json +27 -27
  14. package/pipeline/skills/shared/external/app-clips/references/data-handoff-notifications-location.md +150 -83
  15. package/pipeline/skills/shared/external/app-clips/references/routing-and-experiences.md +135 -83
  16. package/pipeline/skills/shared/external/app-clips/references/size-capabilities-and-promotion.md +143 -85
  17. package/pipeline/skills/shared/external/app-intents/SKILL.md +302 -304
  18. package/pipeline/skills/shared/external/app-intents/evals/evals.json +21 -21
  19. package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +594 -894
  20. package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +339 -277
  21. package/pipeline/skills/shared/external/app-store-optimization/evals/evals.json +27 -23
  22. package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +105 -122
  23. package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +143 -166
  24. package/pipeline/skills/shared/external/app-store-review/SKILL.md +307 -326
  25. package/pipeline/skills/shared/external/app-store-review/evals/evals.json +21 -21
  26. package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +105 -67
  27. package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +114 -101
  28. package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +333 -360
  29. package/pipeline/skills/shared/external/apple-on-device-ai/evals/evals.json +24 -27
  30. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +215 -322
  31. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +161 -256
  32. package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +277 -387
  33. package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +196 -210
  34. package/pipeline/skills/shared/external/authentication/SKILL.md +265 -381
  35. package/pipeline/skills/shared/external/authentication/evals/evals.json +25 -25
  36. package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +133 -178
  37. package/pipeline/skills/shared/external/authentication/references/passkeys.md +111 -147
  38. package/pipeline/skills/shared/external/avkit/SKILL.md +267 -364
  39. package/pipeline/skills/shared/external/avkit/evals/evals.json +26 -26
  40. package/pipeline/skills/shared/external/avkit/references/avkit-patterns.md +375 -493
  41. package/pipeline/skills/shared/external/background-processing/SKILL.md +270 -382
  42. package/pipeline/skills/shared/external/background-processing/evals/evals.json +22 -22
  43. package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +169 -317
  44. package/pipeline/skills/shared/external/callkit-voip/SKILL.md +290 -371
  45. package/pipeline/skills/shared/external/callkit-voip/evals/evals.json +24 -24
  46. package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +175 -343
  47. package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +292 -381
  48. package/pipeline/skills/shared/external/cloudkit-sync/evals/evals.json +33 -30
  49. package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +227 -355
  50. package/pipeline/skills/shared/external/contacts-framework/SKILL.md +197 -346
  51. package/pipeline/skills/shared/external/contacts-framework/evals/evals.json +19 -21
  52. package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +169 -308
  53. package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +226 -376
  54. package/pipeline/skills/shared/external/core-bluetooth/evals/evals.json +25 -22
  55. package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +257 -337
  56. package/pipeline/skills/shared/external/core-data/SKILL.md +292 -368
  57. package/pipeline/skills/shared/external/core-data/evals/evals.json +30 -27
  58. package/pipeline/skills/shared/external/core-motion/SKILL.md +235 -324
  59. package/pipeline/skills/shared/external/core-motion/evals/evals.json +31 -27
  60. package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +210 -310
  61. package/pipeline/skills/shared/external/core-nfc/SKILL.md +292 -366
  62. package/pipeline/skills/shared/external/core-nfc/evals/evals.json +22 -24
  63. package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +233 -329
  64. package/pipeline/skills/shared/external/coreml/SKILL.md +323 -367
  65. package/pipeline/skills/shared/external/coreml/evals/evals.json +24 -21
  66. package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +562 -565
  67. package/pipeline/skills/shared/external/cryptokit/SKILL.md +253 -394
  68. package/pipeline/skills/shared/external/cryptokit/evals/evals.json +20 -18
  69. package/pipeline/skills/shared/external/cryptokit/references/cryptokit-patterns.md +299 -488
  70. package/pipeline/skills/shared/external/debugging-instruments/SKILL.md +270 -323
  71. package/pipeline/skills/shared/external/debugging-instruments/evals/evals.json +27 -30
  72. package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +167 -315
  73. package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +140 -193
  74. package/pipeline/skills/shared/external/device-integrity/SKILL.md +230 -353
  75. package/pipeline/skills/shared/external/device-integrity/evals/evals.json +25 -21
  76. package/pipeline/skills/shared/external/device-integrity/references/device-integrity-patterns.md +159 -197
  77. package/pipeline/skills/shared/external/energykit/SKILL.md +225 -392
  78. package/pipeline/skills/shared/external/energykit/evals/evals.json +29 -28
  79. package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +174 -470
  80. package/pipeline/skills/shared/external/eventkit-calendar/SKILL.md +261 -383
  81. package/pipeline/skills/shared/external/eventkit-calendar/evals/evals.json +25 -22
  82. package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +165 -268
  83. package/pipeline/skills/shared/external/healthkit/SKILL.md +252 -303
  84. package/pipeline/skills/shared/external/healthkit/evals/evals.json +24 -23
  85. package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +369 -523
  86. package/pipeline/skills/shared/external/homekit-matter/SKILL.md +233 -348
  87. package/pipeline/skills/shared/external/homekit-matter/evals/evals.json +27 -22
  88. package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +199 -305
  89. package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +368 -340
  90. package/pipeline/skills/shared/external/ios-accessibility/evals/evals.json +28 -27
  91. package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +314 -260
  92. package/pipeline/skills/shared/external/ios-accessibility/references/media-accessibility.md +97 -67
  93. package/pipeline/skills/shared/external/ios-accessibility/references/nutrition-labels.md +165 -101
  94. package/pipeline/skills/shared/external/ios-localization/SKILL.md +258 -371
  95. package/pipeline/skills/shared/external/ios-localization/evals/evals.json +23 -23
  96. package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +283 -491
  97. package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +313 -440
  98. package/pipeline/skills/shared/external/ios-networking/SKILL.md +265 -341
  99. package/pipeline/skills/shared/external/ios-networking/evals/evals.json +24 -24
  100. package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +425 -652
  101. package/pipeline/skills/shared/external/ios-networking/references/file-storage-patterns.md +143 -285
  102. package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +93 -53
  103. package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +231 -456
  104. package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +517 -784
  105. package/pipeline/skills/shared/external/ios-simulator/SKILL.md +265 -393
  106. package/pipeline/skills/shared/external/ios-simulator/evals/evals.json +21 -21
  107. package/pipeline/skills/shared/external/ios-simulator/references/simctl-commands.md +177 -270
  108. package/pipeline/skills/shared/external/live-activities/SKILL.md +318 -360
  109. package/pipeline/skills/shared/external/live-activities/evals/evals.json +21 -21
  110. package/pipeline/skills/shared/external/live-activities/references/activitykit-patterns.md +478 -710
  111. package/pipeline/skills/shared/external/mapkit-location/SKILL.md +295 -267
  112. package/pipeline/skills/shared/external/mapkit-location/evals/evals.json +28 -24
  113. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-corelocation-patterns.md +378 -532
  114. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +397 -499
  115. package/pipeline/skills/shared/external/metrickit-diagnostics/SKILL.md +165 -348
  116. package/pipeline/skills/shared/external/metrickit-diagnostics/evals/evals.json +26 -23
  117. package/pipeline/skills/shared/external/metrickit-diagnostics/references/metrickit-patterns.md +123 -130
  118. package/pipeline/skills/shared/external/musickit-audio/SKILL.md +189 -315
  119. package/pipeline/skills/shared/external/musickit-audio/evals/evals.json +22 -21
  120. package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +181 -270
  121. package/pipeline/skills/shared/external/natural-language/SKILL.md +188 -340
  122. package/pipeline/skills/shared/external/natural-language/evals/evals.json +21 -21
  123. package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +171 -225
  124. package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +258 -392
  125. package/pipeline/skills/shared/external/passkit-wallet/evals/evals.json +30 -29
  126. package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +164 -231
  127. package/pipeline/skills/shared/external/pdfkit/SKILL.md +312 -344
  128. package/pipeline/skills/shared/external/pdfkit/evals/evals.json +19 -19
  129. package/pipeline/skills/shared/external/pdfkit/references/pdfkit-patterns.md +413 -624
  130. package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +242 -358
  131. package/pipeline/skills/shared/external/pencilkit-drawing/evals/evals.json +25 -21
  132. package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +161 -226
  133. package/pipeline/skills/shared/external/permissionkit/SKILL.md +282 -400
  134. package/pipeline/skills/shared/external/permissionkit/evals/evals.json +27 -30
  135. package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +237 -350
  136. package/pipeline/skills/shared/external/photos-camera-media/SKILL.md +276 -325
  137. package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +299 -545
  138. package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +344 -588
  139. package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +316 -660
  140. package/pipeline/skills/shared/external/photos-camera-media/references/photokit-patterns.md +270 -416
  141. package/pipeline/skills/shared/external/push-notifications/SKILL.md +312 -340
  142. package/pipeline/skills/shared/external/push-notifications/evals/evals.json +27 -26
  143. package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +328 -485
  144. package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +327 -560
  145. package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +218 -410
  146. package/pipeline/skills/shared/external/realitykit-ar/evals/evals.json +24 -27
  147. package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +221 -348
  148. package/pipeline/skills/shared/external/shareplay-activities/SKILL.md +222 -393
  149. package/pipeline/skills/shared/external/shareplay-activities/evals/evals.json +23 -24
  150. package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +280 -420
  151. package/pipeline/skills/shared/external/speech-recognition/SKILL.md +217 -421
  152. package/pipeline/skills/shared/external/speech-recognition/evals/evals.json +23 -26
  153. package/pipeline/skills/shared/external/speech-recognition/references/speechanalyzer-patterns.md +133 -125
  154. package/pipeline/skills/shared/external/storekit/SKILL.md +228 -204
  155. package/pipeline/skills/shared/external/storekit/evals/evals.json +27 -24
  156. package/pipeline/skills/shared/external/storekit/references/app-review-guidelines.md +98 -109
  157. package/pipeline/skills/shared/external/storekit/references/core-patterns.md +298 -242
  158. package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +356 -649
  159. package/pipeline/skills/shared/external/swift-api-design-guidelines/SKILL.md +274 -399
  160. package/pipeline/skills/shared/external/swift-api-design-guidelines/evals/evals.json +22 -24
  161. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/argument-labels-and-parameters.md +107 -108
  162. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md +93 -165
  163. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md +99 -137
  164. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md +77 -120
  165. package/pipeline/skills/shared/external/swift-architecture/SKILL.md +334 -350
  166. package/pipeline/skills/shared/external/swift-architecture/evals/evals.json +22 -22
  167. package/pipeline/skills/shared/external/swift-charts/SKILL.md +208 -394
  168. package/pipeline/skills/shared/external/swift-charts/evals/evals.json +27 -30
  169. package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +351 -762
  170. package/pipeline/skills/shared/external/swift-codable/SKILL.md +339 -343
  171. package/pipeline/skills/shared/external/swift-codable/evals/evals.json +20 -20
  172. package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +303 -351
  173. package/pipeline/skills/shared/external/swift-concurrency/evals/evals.json +27 -24
  174. package/pipeline/skills/shared/external/swift-concurrency/references/approachable-concurrency.md +65 -80
  175. package/pipeline/skills/shared/external/swift-concurrency/references/async-algorithms.md +48 -84
  176. package/pipeline/skills/shared/external/swift-concurrency/references/bridging-interop.md +134 -79
  177. package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +145 -167
  178. package/pipeline/skills/shared/external/swift-concurrency/references/diagnostics.md +62 -50
  179. package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +92 -121
  180. package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +177 -241
  181. package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +258 -234
  182. package/pipeline/skills/shared/external/swift-language/SKILL.md +342 -382
  183. package/pipeline/skills/shared/external/swift-language/evals/evals.json +24 -27
  184. package/pipeline/skills/shared/external/swift-language/references/swift-attributes-interop.md +79 -56
  185. package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +297 -340
  186. package/pipeline/skills/shared/external/swift-security/SKILL.md +180 -161
  187. package/pipeline/skills/shared/external/swift-security/evals/evals.json +25 -25
  188. package/pipeline/skills/shared/external/swift-security/references/biometric-authentication.md +314 -469
  189. package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +408 -476
  190. package/pipeline/skills/shared/external/swift-security/references/common-anti-patterns.md +260 -530
  191. package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md +270 -477
  192. package/pipeline/skills/shared/external/swift-security/references/credential-storage-patterns.md +573 -571
  193. package/pipeline/skills/shared/external/swift-security/references/cryptokit-public-key.md +370 -441
  194. package/pipeline/skills/shared/external/swift-security/references/cryptokit-symmetric.md +332 -433
  195. package/pipeline/skills/shared/external/swift-security/references/keychain-access-control.md +346 -468
  196. package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +352 -472
  197. package/pipeline/skills/shared/external/swift-security/references/keychain-item-classes.md +431 -432
  198. package/pipeline/skills/shared/external/swift-security/references/keychain-sharing.md +328 -425
  199. package/pipeline/skills/shared/external/swift-security/references/migration-legacy-stores.md +341 -579
  200. package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +396 -457
  201. package/pipeline/skills/shared/external/swift-security/references/testing-security-code.md +354 -614
  202. package/pipeline/skills/shared/external/swift-testing/SKILL.md +188 -175
  203. package/pipeline/skills/shared/external/swift-testing/evals/evals.json +26 -24
  204. package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +80 -84
  205. package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +317 -433
  206. package/pipeline/skills/shared/external/swiftdata/SKILL.md +392 -256
  207. package/pipeline/skills/shared/external/swiftdata/evals/evals.json +24 -24
  208. package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +206 -402
  209. package/pipeline/skills/shared/external/swiftdata/references/indexing.md +59 -52
  210. package/pipeline/skills/shared/external/swiftdata/references/predicate-pitfalls.md +57 -33
  211. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +354 -747
  212. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +300 -508
  213. package/pipeline/skills/shared/external/swiftlint/SKILL.md +175 -226
  214. package/pipeline/skills/shared/external/swiftlint/references/adoption-and-configuration.md +141 -208
  215. package/pipeline/skills/shared/external/swiftlint/references/custom-rules-and-analyze.md +100 -109
  216. package/pipeline/skills/shared/external/swiftlint/references/plugins-run-scripts-and-integrations.md +159 -179
  217. package/pipeline/skills/shared/external/swiftlint/references/rule-reference.md +383 -18
  218. package/pipeline/skills/shared/external/swiftlint/references/rules-suppressions-and-baselines.md +143 -229
  219. package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +283 -366
  220. package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +396 -608
  221. package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +336 -385
  222. package/pipeline/skills/shared/external/swiftui-gestures/SKILL.md +239 -349
  223. package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +228 -310
  224. package/pipeline/skills/shared/external/swiftui-layout-components/SKILL.md +260 -249
  225. package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +92 -74
  226. package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +112 -177
  227. package/pipeline/skills/shared/external/swiftui-layout-components/references/list.md +61 -64
  228. package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +94 -134
  229. package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +193 -225
  230. package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +173 -327
  231. package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +193 -168
  232. package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +127 -150
  233. package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +132 -133
  234. package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +152 -117
  235. package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +106 -140
  236. package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +316 -252
  237. package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +341 -332
  238. package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +547 -854
  239. package/pipeline/skills/shared/external/swiftui-patterns/references/design-polish.md +485 -537
  240. package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +417 -499
  241. package/pipeline/skills/shared/external/swiftui-performance/SKILL.md +213 -376
  242. package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +86 -175
  243. package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +89 -195
  244. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +95 -182
  245. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-improving-swiftui-performance.md +71 -149
  246. package/pipeline/skills/shared/external/swiftui-performance/references/wwdc-session-sources.md +21 -27
  247. package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +303 -295
  248. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +204 -387
  249. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +469 -683
  250. package/pipeline/skills/shared/external/swiftui-webkit/SKILL.md +140 -186
  251. package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +75 -86
  252. package/pipeline/skills/shared/external/swiftui-webkit/references/local-content-and-custom-schemes.md +63 -60
  253. package/pipeline/skills/shared/external/swiftui-webkit/references/migration-and-fallbacks.md +69 -137
  254. package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +95 -67
  255. package/pipeline/skills/shared/external/tipkit/SKILL.md +220 -335
  256. package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +356 -494
  257. package/pipeline/skills/shared/external/vision-framework/SKILL.md +260 -375
  258. package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +393 -515
  259. package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +363 -539
  260. package/pipeline/skills/shared/external/weatherkit/SKILL.md +152 -310
  261. package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +288 -407
  262. package/pipeline/skills/shared/external/widgetkit/SKILL.md +216 -288
  263. package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +414 -719
  264. package/pipeline/skills/shared/external/NOTICE-swift-ios-skills.md +0 -39
@@ -1,496 +1,399 @@
1
- # Keychain Sharing: Access Groups, Extensions, and Cross-Device Sync
1
+ # Keychain Sharing Across Apps and Extensions
2
2
 
3
- > Scope: Access-group design and entitlement correctness for sharing keychain items across app targets, extensions, and devices.
3
+ Access groups are the one mechanism for letting several apps and extensions read the
4
+ same Keychain items. Getting them right takes three things together: the exact Team ID
5
+ prefix, an entitlement on every target that participates, and an explicit
6
+ `kSecAttrAccessGroup` in the code.
4
7
 
5
- Keychain access groups are the sole mechanism for sharing credentials between apps and extensions on Apple platforms. Correct configuration requires exact Team ID prefixes, per-target entitlements, and explicit `kSecAttrAccessGroup` usage in code - three requirements that most AI-generated code gets wrong. This reference covers access group mechanics, the two entitlement systems, correct and incorrect Swift patterns, macOS-specific requirements, iCloud sync, platform edge cases, and debugging strategies. All guidance reflects current behavior through iOS 18, macOS Sequoia 15, and the 2025-2026 developer landscape.
6
-
7
- **Authoritative sources:** Apple "Sharing Access to Keychain Items Among a Collection of Apps" documentation, TN3137 "On Mac Keychain APIs and Implementations," Apple Platform Security Guide (iCloud Keychain syncing), Quinn "The Eskimo!" DTS forum posts "SecItem: Fundamentals" and "SecItem: Pitfalls and Best Practices" (updated May 2025), Configuring Keychain Sharing documentation.
8
-
9
- ---
8
+ Behaviour described here holds through iOS 18 and macOS Sequoia 15. It is based on
9
+ Apple's "Sharing Access to Keychain Items Among a Collection of Apps", TN3137 (On Mac
10
+ keychain APIs and implementations), the iCloud Keychain chapter of the Platform
11
+ Security Guide, "Configuring Keychain Sharing", and guidance from Apple DTS engineers
12
+ on the developer forums (updated May 2025).
10
13
 
11
14
  ## Contents
12
15
 
13
- - [How Access Groups Work](#how-access-groups-work)
14
- - [Two Entitlements, Two Formats, Different Purposes](#two-entitlements-two-formats-different-purposes)
15
- - [Keychain Sharing (`keychain-access-groups`)](#keychain-sharing-keychain-access-groups)
16
- - [App Groups (`com.apple.security.application-groups`)](#app-groups-comapplesecurityapplication-groups)
17
- - [Comparison Table](#comparison-table)
18
- - [Code Patterns: Correct and Incorrect](#code-patterns-correct-and-incorrect)
19
- - [Storing an item with an explicit access group](#storing-an-item-with-an-explicit-access-group)
20
- - [Access group without Team ID prefix (most common AI mistake)](#access-group-without-team-id-prefix-most-common-ai-mistake)
21
- - [App extension reading a shared keychain item](#app-extension-reading-a-shared-keychain-item)
22
- - [Extension that fails because it lacks the entitlement](#extension-that-fails-because-it-lacks-the-entitlement)
23
- - [iCloud Keychain sync with `kSecAttrSynchronizable`](#icloud-keychain-sync-with-ksecattrsynchronizable)
24
- - [Assuming items sync by default](#assuming-items-sync-by-default)
25
- - [Cross-Target Entitlements Setup](#cross-target-entitlements-setup)
26
- - [Xcode Configuration Steps](#xcode-configuration-steps)
27
- - [Required Entitlements Matrix](#required-entitlements-matrix)
28
- - [The macOS Keychain Split](#the-macos-keychain-split)
29
- - [Cross-platform macOS support with `kSecUseDataProtectionKeychain`](#cross-platform-macos-support-with-ksecusedataprotectionkeychain)
30
- - [Migrating Items Between Access Groups](#migrating-items-between-access-groups)
31
- - [Lifecycle Edge Cases](#lifecycle-edge-cases)
32
- - [Keychain items persist after app uninstall](#keychain-items-persist-after-app-uninstall)
33
- - [App transfers between teams break keychain access](#app-transfers-between-teams-break-keychain-access)
34
- - [Cross-developer sharing is impossible via access groups](#cross-developer-sharing-is-impossible-via-access-groups)
35
- - [Platform-Specific Patterns](#platform-specific-patterns)
36
- - [watchOS](#watchos)
37
- - [Widget Extensions (WidgetKit)](#widget-extensions-widgetkit)
38
- - [Build and Distribution Considerations](#build-and-distribution-considerations)
39
- - [Debugging When Keychain Sharing Breaks](#debugging-when-keychain-sharing-breaks)
40
- - [Essential Error Codes](#essential-error-codes)
41
- - [Debugging Checklist](#debugging-checklist)
42
- - [Test Matrix](#test-matrix)
43
- - [Security Threat Model Notes](#security-threat-model-notes)
44
- - [What Changed in 2024-2026](#what-changed-in-20242026)
45
- - [Cross-References](#cross-references)
46
- - [Conclusion](#conclusion)
47
- - [Summary Checklist](#summary-checklist)
48
-
49
- ## How Access Groups Work
50
-
51
- Every app belongs to one or more **access groups** - string identifiers that tag which processes can read and write specific keychain items. An app can belong to many groups, but each keychain item belongs to **exactly one**. The `securityd` daemon enforces access by checking the calling process's entitlements against the item's group at runtime.
52
-
53
- The system constructs a virtual array of access groups for each app by concatenating three sources **in this exact order**:
54
-
55
- 1. **Keychain access groups** from the `keychain-access-groups` entitlement
56
- 2. **Application identifier** - automatically generated as `TeamID.BundleID` (e.g., `SKMME9E2Y8.com.example.MyApp`)
57
- 3. **App groups** from the `com.apple.security.application-groups` entitlement (iOS 8+)
58
-
59
- **The first item in this concatenated list becomes the default access group.** When `SecItemAdd` is called without specifying `kSecAttrAccessGroup`, the item lands in that default group. When `SecItemCopyMatching` is called without specifying a group, the search spans **all** groups the app belongs to. This ordering means a keychain access group can be the default (it appears first), but an app group can never be the default because the application identifier always precedes it.
60
-
61
- Example for an app with one keychain group and one app group:
62
-
63
- ```text
64
- [SKMME9E2Y8.com.example.SharedItems, ← keychain access group (default)
65
- SKMME9E2Y8.com.example.MyApp, ← application identifier (automatic)
66
- group.com.example.AppSuite] ← app group
67
- ```
68
-
69
- **Sharing is restricted to a single development team.** Apps from different developer teams cannot share keychain items through access groups. The Team ID prefix on every group identifier, enforced through code-signed provisioning profiles, prevents cross-team access. The only way different developers' apps can share credentials is through iCloud Keychain + Associated Domains (password autofill based on web domain ownership), which is an entirely different mechanism.
70
-
71
- ---
72
-
73
- ## Two Entitlements, Two Formats, Different Purposes
16
+ - [How access groups work](#how-access-groups-work)
17
+ - [Two entitlements with two formats](#two-entitlements-with-two-formats)
18
+ - [Code](#code)
19
+ - [Setting up every target](#setting-up-every-target)
20
+ - [macOS has two keychains](#macos-has-two-keychains)
21
+ - [Moving an item to another group](#moving-an-item-to-another-group)
22
+ - [Lifecycle surprises](#lifecycle-surprises)
23
+ - [Platform notes](#platform-notes)
24
+ - [Signing and distribution](#signing-and-distribution)
25
+ - [When sharing breaks](#when-sharing-breaks)
26
+ - [Threat model](#threat-model)
27
+ - [2024 to 2026](#2024-to-2026)
28
+ - [Related](#related)
29
+ - [Checklist](#checklist)
74
30
 
75
- The most common developer mistake is **confusing Keychain Sharing with App Groups**. These are separate capabilities with different entitlement keys, different identifier formats, and different scopes.
31
+ ## How access groups work
76
32
 
77
- ### Keychain Sharing (`keychain-access-groups`)
33
+ An app can belong to many access groups. Every item belongs to exactly one.
34
+ `securityd` compares the calling process's entitlements with the item's group on every
35
+ call.
78
36
 
79
- This entitlement exists solely for sharing keychain items between apps. Identifiers are prefixed with the Team ID:
37
+ The system builds the app's list of groups in this order:
80
38
 
81
- ```xml
82
- <?xml version="1.0" encoding="UTF-8"?>
83
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
84
- "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
85
- <plist version="1.0">
86
- <dict>
87
- <key>keychain-access-groups</key>
88
- <array>
89
- <string>$(AppIdentifierPrefix)com.example.SharedItems</string>
90
- </array>
91
- </dict>
92
- </plist>
93
- ```
39
+ 1. Entries of the `keychain-access-groups` entitlement.
40
+ 2. The application identifier, `TeamID.BundleID`, added automatically.
41
+ 3. Entries of `com.apple.security.application-groups` (iOS 8 and later).
94
42
 
95
- The `$(AppIdentifierPrefix)` build variable resolves at signing time to the Team ID followed by a dot (e.g., `SKMME9E2Y8.`). In code, the fully resolved string is required - `"SKMME9E2Y8.com.example.SharedItems"` - not just `"com.example.SharedItems"`.
43
+ The first entry in that list is the default: `SecItemAdd` without
44
+ `kSecAttrAccessGroup` puts the item there. `SecItemCopyMatching` without a group
45
+ searches all of the app's groups.
96
46
 
97
- ### App Groups (`com.apple.security.application-groups`)
47
+ Because the application identifier always comes before app groups, a keychain access
48
+ group can be the default but an app group never can.
98
49
 
99
- App Groups share more than keychain items: shared file containers, `UserDefaults(suiteName:)`, and IPC. The identifier uses a `group.` prefix with **no Team ID**:
50
+ Sharing is limited to one development team; the signed provisioning profile enforces
51
+ the Team ID prefix. Between different teams the only route is iCloud Keychain
52
+ together with Associated Domains, which serves password autofill rather than shared
53
+ app data.
100
54
 
101
- ```xml
102
- <?xml version="1.0" encoding="UTF-8"?>
103
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
104
- "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
105
- <plist version="1.0">
106
- <dict>
107
- <key>com.apple.security.application-groups</key>
108
- <array>
109
- <string>group.com.example.AppSuite</string>
110
- </array>
111
- </dict>
112
- </plist>
113
- ```
55
+ ## Two entitlements with two formats
114
56
 
115
- Since iOS 8, app group names double as keychain access groups - `"group.com.example.AppSuite"` can be used as the `kSecAttrAccessGroup` value. However, App Groups appear last in the access group array and **can never be the default group for new items**. A critical macOS caveat: **app groups cannot be used as keychain access groups on macOS** - this is an iOS/iPadOS-only feature.
57
+ Mixing up Keychain Sharing and App Groups is the most common mistake.
116
58
 
117
- ### Comparison Table
59
+ | | Keychain Sharing | App Groups |
60
+ | --- | --- | --- |
61
+ | Entitlement | `keychain-access-groups` | `com.apple.security.application-groups` |
62
+ | Value in entitlements | `$(AppIdentifierPrefix)com.example.vault` | `group.com.example.suite` |
63
+ | Resolved value | `A1B2C3D4E5.com.example.vault` | `group.com.example.suite` (no Team ID) |
64
+ | Shares | Keychain items | Containers, `UserDefaults(suiteName:)`, IPC, and on iOS also Keychain items |
118
65
 
119
- | Aspect | Keychain Sharing | App Groups |
120
- | -------------------------- | ------------------------------------------ | ------------------------------------------------------------ |
121
- | **Entitlement key** | `keychain-access-groups` | `com.apple.security.application-groups` |
122
- | **Format** | `$(AppIdentifierPrefix)com.example.shared` | `group.com.example.shared` |
123
- | **Team ID prefix** | Yes (automatic via build variable) | No (`group.` prefix instead) |
124
- | **Shares** | Keychain items only | Containers, UserDefaults, IPC, and keychain items (iOS only) |
125
- | **Can be default group** | Yes (if first in array) | No |
126
- | **macOS keychain sharing** | Yes (with data protection keychain) | No |
66
+ `$(AppIdentifierPrefix)` is replaced with `TEAMID.` at signing time. Code never sees
67
+ that substitution, so it must use the full resolved string.
127
68
 
128
- Both entitlements can be used simultaneously. If only keychain sharing is needed, use Keychain Sharing. If App Groups are already in use for shared UserDefaults or file containers, they can piggyback for keychain sharing on iOS - but always specify `kSecAttrAccessGroup` explicitly.
69
+ Since iOS 8 an app group name can be used as a `kSecAttrAccessGroup` value, though
70
+ never as the default. On macOS app groups cannot act as keychain access groups; that
71
+ is iOS and iPadOS only. On macOS, Keychain Sharing groups work only in the data
72
+ protection keychain.
129
73
 
130
- ---
74
+ Both entitlements can coexist. If you only need shared credentials, add Keychain
75
+ Sharing. If the apps already share an App Group, it can carry Keychain items on iOS as
76
+ long as every call names it explicitly.
131
77
 
132
- ## Code Patterns: Correct and Incorrect
78
+ ## Code
133
79
 
134
- ### Storing an item with an explicit access group
80
+ Writing to a shared group:
135
81
 
136
82
  ```swift
137
83
  import Security
138
84
 
139
- let teamID = "SKMME9E2Y8"
140
- let accessGroup = "\(teamID).com.example.SharedItems"
141
-
142
- let password = "s3cretT0ken".data(using: .utf8)!
143
- let addQuery: [String: Any] = [
144
- kSecClass as String: kSecClassGenericPassword,
145
- kSecAttrService as String: "com.example.authService",
146
- kSecAttrAccount as String: "user@example.com",
147
- kSecAttrAccessGroup as String: accessGroup,
148
- kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly,
149
- kSecValueData as String: password
150
- ]
151
-
152
- let status = SecItemAdd(addQuery as CFDictionary, nil)
153
- guard status == errSecSuccess else {
154
- print("Keychain add failed: \(status)") // -34018 = missing entitlement
155
- return
85
+ enum SharedVault {
86
+ static let group = "A1B2C3D4E5.com.example.vault"
87
+ static let service = "com.example.vault.session"
156
88
  }
157
- ```
158
-
159
- The Team ID must be the **literal 10-character string** from the Apple Developer account, not a build variable - `$(AppIdentifierPrefix)` only works in entitlements plists, not in Swift code.
160
89
 
161
- ### Access group without Team ID prefix (most common AI mistake)
162
-
163
- ```swift
164
- // ❌ WRONG - Missing Team ID prefix
165
- let accessGroup = "com.example.SharedItems"
166
-
167
- let addQuery: [String: Any] = [
168
- kSecClass as String: kSecClassGenericPassword,
169
- kSecAttrService as String: "com.example.authService",
170
- kSecAttrAccount as String: "user@example.com",
171
- kSecAttrAccessGroup as String: accessGroup, // Will fail!
172
- kSecValueData as String: password
173
- ]
174
- // Returns errSecMissingEntitlement (-34018) on iOS 13+
175
- // Returns errSecItemNotFound (-25300) on older versions
176
- ```
177
-
178
- Xcode's Keychain Sharing UI shows `com.example.SharedItems` without the prefix, which misleads developers and AI generators alike. **In code, the full `TEAMID.com.example.SharedItems` string is always required.**
179
-
180
- ### App extension reading a shared keychain item
181
-
182
- The extension target must have its own Keychain Sharing capability with the same group:
183
-
184
- ```swift
185
- // In a widget extension, share extension, or other app extension
186
- let teamID = "SKMME9E2Y8"
187
- let accessGroup = "\(teamID).com.example.SharedItems"
188
-
189
- let readQuery: [String: Any] = [
190
- kSecClass as String: kSecClassGenericPassword,
191
- kSecAttrService as String: "com.example.authService",
192
- kSecAttrAccount as String: "user@example.com",
193
- kSecAttrAccessGroup as String: accessGroup,
194
- kSecReturnData as String: true
195
- ]
196
-
197
- var result: AnyObject?
198
- let status = SecItemCopyMatching(readQuery as CFDictionary, &result)
199
- if status == errSecSuccess, let data = result as? Data {
200
- let token = String(data: data, encoding: .utf8)
201
- // Use the shared token
90
+ func publishSessionToken(_ token: Data) -> OSStatus {
91
+ let identity: [String: Any] = [
92
+ kSecClass as String: kSecClassGenericPassword,
93
+ kSecAttrAccessGroup as String: SharedVault.group,
94
+ kSecAttrService as String: SharedVault.service,
95
+ kSecAttrAccount as String: "active-user"
96
+ ]
97
+ var insert = identity
98
+ insert[kSecValueData as String] = token
99
+ insert[kSecAttrAccessible as String] = kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
100
+
101
+ let added = SecItemAdd(insert as CFDictionary, nil)
102
+ guard added == errSecDuplicateItem else { return added } // -34018: entitlement missing
103
+ let changes: [String: Any] = [kSecValueData as String: token]
104
+ return SecItemUpdate(identity as CFDictionary, changes as CFDictionary)
202
105
  }
203
106
  ```
204
107
 
205
- ### Extension that fails because it lacks the entitlement
108
+ The Team ID in code is the literal ten-character string. `$(AppIdentifierPrefix)`
109
+ works only inside the entitlements plist.
206
110
 
207
111
  ```swift
208
- // ❌ This code is syntactically correct, but the extension target is
209
- // missing the Keychain Sharing capability in Xcode → Signing & Capabilities.
210
- // The main app has it, but extensions are SEPARATE executable targets.
211
- // Result: errSecMissingEntitlement (-34018)
112
+ // Broken: group without the Team ID prefix
113
+ let wrongGroup = "com.example.vault"
212
114
  ```
213
115
 
214
- **Each executable target - main app, widget extension, share extension, notification extension - needs its own Keychain Sharing entitlement.** Frameworks do not have entitlements; only the targets linking them do. In Xcode: select the extension target → Signing & Capabilities → + Capability → Keychain Sharing → add the same group name.
116
+ This fails with `errSecMissingEntitlement` (-34018) on iOS 13 and later, and with
117
+ `errSecItemNotFound` (-25300) on earlier versions. Xcode's Keychain Sharing editor
118
+ shows the name without its prefix, which is why this keeps happening.
215
119
 
216
- ### iCloud Keychain sync with `kSecAttrSynchronizable`
120
+ Reading it from an extension:
217
121
 
218
122
  ```swift
219
- let syncQuery: [String: Any] = [
220
- kSecClass as String: kSecClassGenericPassword,
221
- kSecAttrService as String: "com.example.authService",
222
- kSecAttrAccount as String: "user@example.com",
223
- kSecAttrAccessGroup as String: "\(teamID).com.example.SharedItems",
224
- kSecAttrSynchronizable as String: kCFBooleanTrue!,
225
- kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlock,
226
- kSecValueData as String: password
227
- ]
228
- let status = SecItemAdd(syncQuery as CFDictionary, nil)
123
+ func readSessionTokenInExtension() -> Data? {
124
+ let query: [String: Any] = [
125
+ kSecClass as String: kSecClassGenericPassword,
126
+ kSecAttrAccessGroup as String: SharedVault.group,
127
+ kSecAttrService as String: SharedVault.service,
128
+ kSecAttrAccount as String: "active-user",
129
+ kSecReturnData as String: true,
130
+ kSecMatchLimit as String: kSecMatchLimitOne
131
+ ]
132
+ var found: CFTypeRef?
133
+ guard SecItemCopyMatching(query as CFDictionary, &found) == errSecSuccess else { return nil }
134
+ return found as? Data
135
+ }
229
136
  ```
230
137
 
231
- **Critical constraints:**
232
-
233
- - Synchronizable items **cannot** use `kSecAttrAccessible` values ending in `ThisDeviceOnly` - the item would never sync. Attempting this silently fails to sync across devices.
234
- - When querying for synchronizable items, include `kSecAttrSynchronizable: true` or `kSecAttrSynchronizableAny` - otherwise the search excludes them.
235
- - The user must have iCloud Keychain enabled and be signed into the same Apple ID on all target devices.
236
- - Synchronization is orthogonal to on-device sharing: an item can be both in a shared access group and synchronizable across devices.
138
+ An extension that lacks its own Keychain Sharing capability gets -34018 here even
139
+ when the host app has the capability. Every executable target (app, widget, share
140
+ extension, notification service extension) needs the entitlement itself. Frameworks
141
+ carry no entitlements; only the targets that link them do.
237
142
 
238
- ```swift
239
- // ✅ Query that finds both sync and non-sync items
240
- let findQuery: [String: Any] = [
241
- kSecClass as String: kSecClassGenericPassword,
242
- kSecAttrService as String: "com.example.authService",
243
- kSecAttrSynchronizable as String: kSecAttrSynchronizableAny,
244
- kSecReturnData as String: true
245
- ]
246
- ```
143
+ ### iCloud sync
247
144
 
248
- ### Assuming items sync by default
145
+ Syncing is a separate, per-item opt-in:
249
146
 
250
147
  ```swift
251
- // ❌ WRONG - This item will NOT sync to iCloud Keychain.
252
- // kSecAttrSynchronizable defaults to false when omitted.
253
- let addQuery: [String: Any] = [
254
- kSecClass as String: kSecClassGenericPassword,
255
- kSecAttrService as String: "com.example.authService",
256
- kSecAttrAccount as String: "user@example.com",
257
- kSecValueData as String: password
258
- // No kSecAttrSynchronizable → stays on this device only
259
- ]
148
+ func publishSyncedPreferenceKey(_ secret: Data) -> OSStatus {
149
+ let identity: [String: Any] = [
150
+ kSecClass as String: kSecClassGenericPassword,
151
+ kSecAttrAccessGroup as String: SharedVault.group,
152
+ kSecAttrService as String: "com.example.vault.sync",
153
+ kSecAttrAccount as String: "reader-settings-key",
154
+ kSecAttrSynchronizable as String: kCFBooleanTrue as Any
155
+ ]
156
+ var insert = identity
157
+ insert[kSecValueData as String] = secret
158
+ insert[kSecAttrAccessible as String] = kSecAttrAccessibleAfterFirstUnlock
159
+
160
+ let added = SecItemAdd(insert as CFDictionary, nil)
161
+ guard added == errSecDuplicateItem else { return added }
162
+ return SecItemUpdate(identity as CFDictionary,
163
+ [kSecValueData as String: secret] as CFDictionary)
164
+ }
260
165
  ```
261
166
 
262
- iCloud Keychain sync is **strictly opt-in per item**. Omitting `kSecAttrSynchronizable` or setting it to `false` means the item exists only on the current device. Synchronized items benefit from end-to-end encryption - Apple cannot decrypt the data.
263
-
264
- ---
265
-
266
- ## Cross-Target Entitlements Setup
167
+ Rules for synced items:
267
168
 
268
- Extensions are separate sandboxed executable targets that do **not** inherit capabilities from their containing app.
169
+ - Apple documents that a synchronizable item may not use an accessibility value
170
+ ending in `ThisDeviceOnly`. `SecItemAdd` rejects the combination with `errSecParam`
171
+ (-50); nothing is stored and nothing syncs. Use `AfterFirstUnlock` or `WhenUnlocked`.
172
+ - Every query, update and delete that should see synced items must include
173
+ `kSecAttrSynchronizable: true` or `kSecAttrSynchronizableAny`; without it, synced
174
+ items are excluded from the match.
175
+ - Sync needs iCloud Keychain turned on and the same Apple Account on each device. It
176
+ is independent of which access group the item is in.
177
+ - `kSecAttrSynchronizable` defaults to false. Nothing syncs unless you ask for it item
178
+ by item. Synced items are end-to-end encrypted; Apple cannot read them.
269
179
 
270
- ### Xcode Configuration Steps
180
+ ## Setting up every target
271
181
 
272
- 1. Select the main application target → Signing & Capabilities → + Capability → Keychain Sharing.
273
- 2. Add the desired group identifier (e.g., `com.example.shared`). Xcode auto-prefixes with Team ID in the entitlements file.
274
- 3. **Repeat for every extension target** - select the extension target, add Keychain Sharing, add the **exact same** group identifier.
275
- 4. For App Groups: add the App Groups capability to each target and use the same `group.` identifier.
182
+ Extensions do not inherit capabilities from the app that contains them.
276
183
 
277
- ### Required Entitlements Matrix
184
+ 1. Select the app target, open Signing & Capabilities, click + Capability, add
185
+ Keychain Sharing and enter the group. Xcode applies the Team ID prefix.
186
+ 2. Do exactly the same for each extension target.
187
+ 3. If App Groups are used too, add the same `group.` identifier to each target.
278
188
 
279
- | Target | `keychain-access-groups` | `application-groups` | Notes |
280
- | --------------------- | --------------------------- | ---------------------------- | ------------------------------------ |
281
- | **Main app** | `TEAMID.com.example.shared` | `group.com.example.appsuite` | First entry defines default group |
282
- | **Share extension** | `TEAMID.com.example.shared` | `group.com.example.appsuite` | Must match exactly |
283
- | **Widget extension** | `TEAMID.com.example.shared` | `group.com.example.appsuite` | Independent signing and provisioning |
284
- | **Notification ext.** | `TEAMID.com.example.shared` | `group.com.example.appsuite` | Same rules apply |
189
+ | Target | keychain-access-groups | application-groups |
190
+ | --- | --- | --- |
191
+ | Main app | `A1B2C3D4E5.com.example.vault` (first entry, so the default) | `group.com.example.suite` |
192
+ | Share extension | `A1B2C3D4E5.com.example.vault` | `group.com.example.suite` |
193
+ | Widget extension | `A1B2C3D4E5.com.example.vault` | `group.com.example.suite` |
194
+ | Notification extension | `A1B2C3D4E5.com.example.vault` | `group.com.example.suite` |
285
195
 
286
- ---
196
+ Each target is signed with its own provisioning profile.
287
197
 
288
- ## The macOS Keychain Split
198
+ ## macOS has two keychains
289
199
 
290
- macOS maintains **two completely separate keychain implementations**, and confusing them is a source of endless bugs. Per Apple's TN3137:
200
+ - **File-based keychain.** The legacy implementation: `.keychain-db` files and
201
+ `SecAccess` ACLs. On macOS it is where `SecItem` calls go by default. It has no
202
+ iCloud Keychain, no biometrics, no Secure Enclave keys and no access groups.
203
+ - **Data protection keychain.** The iOS implementation, on macOS since 10.9 through
204
+ iCloud Keychain. It has access groups, `SecAccessControl`, sync, Touch ID and
205
+ Secure Enclave keys. It is available only in a user login context, so `launchd`
206
+ daemons cannot use it.
291
207
 
292
- **File-based keychain** - the legacy system dating back to Mac OS X. Uses Access Control Lists (`SecAccess`), stores items in `.keychain-db` files, and is the default target for `SecItem` API calls on macOS. Does not support iCloud Keychain, biometrics, Secure Enclave keys, or access groups.
293
-
294
- **Data protection keychain** - originated on iOS and arrived on macOS via iCloud Keychain in 10.9. Uses keychain access groups + `SecAccessControl`, supports iCloud sync, Touch ID/Face ID, and Secure Enclave. Available only in user-login contexts - **`launchd` daemons cannot use it**.
295
-
296
- ### Cross-platform macOS support with `kSecUseDataProtectionKeychain`
208
+ On macOS `kSecAttrAccessGroup` is ignored without an error unless the call targets the
209
+ data protection keychain:
297
210
 
298
211
  ```swift
299
- var query: [String: Any] = [
300
- kSecClass as String: kSecClassGenericPassword,
301
- kSecAttrService as String: "com.example.authService",
302
- kSecAttrAccount as String: "user@example.com",
303
- kSecAttrAccessGroup as String: "\(teamID).com.example.SharedItems",
304
- kSecUseDataProtectionKeychain as String: true,
305
- kSecValueData as String: password
212
+ let macQuery: [String: Any] = [
213
+ kSecClass as String: kSecClassGenericPassword,
214
+ kSecAttrService as String: SharedVault.service,
215
+ kSecAttrAccessGroup as String: SharedVault.group,
216
+ kSecUseDataProtectionKeychain as String: true
306
217
  ]
307
- let status = SecItemAdd(query as CFDictionary, nil)
308
218
  ```
309
219
 
310
- On macOS, `kSecAttrAccessGroup` **is silently ignored** unless the data protection keychain is targeted. Setting `kSecUseDataProtectionKeychain` to `true` opts into iOS-style keychain behavior. On iOS, tvOS, and watchOS this key is ignored (those platforms always use data protection).
311
-
312
- Two ways to target the data protection keychain on macOS: set `kSecUseDataProtectionKeychain` to `true`, or set `kSecAttrSynchronizable` to `true` (which also enables iCloud sync). Mac Catalyst and iOS Apps on Mac use data protection exclusively - the flag is ignored there.
220
+ Either `kSecUseDataProtectionKeychain: true` or `kSecAttrSynchronizable: true` routes
221
+ a call there. Mac Catalyst apps and iOS apps running on a Mac always use it.
313
222
 
314
- | Platform/Runtime | Default keychain | Access groups supported | Required flag |
315
- | ------------------ | ----------------- | ----------------------- | ------------------------------------- |
316
- | **iOS/iPadOS** | Data Protection | Yes | None |
317
- | **Mac Catalyst** | Data Protection | Yes | None |
318
- | **macOS (AppKit)** | Legacy file-based | No (by default) | `kSecUseDataProtectionKeychain: true` |
223
+ | Platform | Default keychain | Access groups by default | Flag needed |
224
+ | --- | --- | --- | --- |
225
+ | iOS, iPadOS, Mac Catalyst | Data protection | Yes | No |
226
+ | macOS (AppKit) | File-based | No | `kSecUseDataProtectionKeychain` |
319
227
 
320
- Apple's TN3137 states the file-based keychain is **"on the road to deprecation."** `SecKeychainCreate` was deprecated in the macOS 12 SDK. New code should target data protection exclusively, with the sole exception of `launchd` daemons that lack a user context.
228
+ TN3137 describes the file-based keychain as on the road to deprecation;
229
+ `SecKeychainCreate` is deprecated as of the macOS 12 SDK. Only `launchd` daemons with no
230
+ user context should keep using it.
321
231
 
322
- ---
232
+ ## Moving an item to another group
323
233
 
324
- ## Migrating Items Between Access Groups
325
-
326
- `kSecAttrAccessGroup` is **immutable** for an existing keychain item - it cannot be changed via `SecItemUpdate`. Migration requires a read-add-delete sequence:
327
-
328
- 1. **Read**: Retrieve the complete item from its original access group via `SecItemCopyMatching`.
329
- 2. **Add**: Call `SecItemAdd` with the new `kSecAttrAccessGroup`.
330
- 3. **Delete**: Only after `SecItemAdd` returns `errSecSuccess`, delete the original item via `SecItemDelete`.
331
-
332
- If the add operation fails, the original item remains untouched, preventing data loss. This pattern is safe because it never deletes until the new copy is confirmed.
333
-
334
- ---
335
-
336
- ## Lifecycle Edge Cases
337
-
338
- ### Keychain items persist after app uninstall
339
-
340
- This behavior is undocumented but has been consistent since iOS's early days. Apple attempted to delete keychain items on app removal in iOS 10.3 beta but rolled it back before release due to compatibility issues. Quinn "The Eskimo!" has warned this behavior **could change without notice**. If shared keychain items exist between App A and App B, deleting App A leaves all shared items intact for App B. Even deleting all apps in a shared group does not remove orphaned items - only a factory reset clears them reliably.
341
-
342
- A common workaround for detecting fresh installs (since `UserDefaults` _are_ wiped on uninstall):
234
+ `SecItemUpdate` cannot change `kSecAttrAccessGroup`. To move an item, read it with
235
+ `SecItemCopyMatching`, add a copy in the new group, and delete the original only after
236
+ that add returned `errSecSuccess`. If the add fails, the original is untouched.
343
237
 
344
238
  ```swift
345
- func clearKeychainOnFreshInstall() {
346
- let hasLaunchedBefore = UserDefaults.standard.bool(forKey: "hasLaunchedBefore")
347
- if !hasLaunchedBefore {
348
- // Scope deletion to specific service/group to avoid nuking shared items
349
- let query: [String: Any] = [
350
- kSecClass as String: kSecClassGenericPassword,
351
- kSecAttrService as String: "com.example.authService"
352
- ]
353
- SecItemDelete(query as CFDictionary)
354
- UserDefaults.standard.set(true, forKey: "hasLaunchedBefore")
355
- }
239
+ func moveItem(account: String, from oldGroup: String, to newGroup: String) -> Bool {
240
+ let source: [String: Any] = [
241
+ kSecClass as String: kSecClassGenericPassword,
242
+ kSecAttrService as String: SharedVault.service,
243
+ kSecAttrAccount as String: account,
244
+ kSecAttrAccessGroup as String: oldGroup
245
+ ]
246
+ var lookup = source
247
+ lookup[kSecReturnData as String] = true
248
+ var value: CFTypeRef?
249
+ guard SecItemCopyMatching(lookup as CFDictionary, &value) == errSecSuccess,
250
+ let secret = value as? Data else { return false }
251
+
252
+ var target = source
253
+ target[kSecAttrAccessGroup as String] = newGroup
254
+ target[kSecValueData as String] = secret
255
+ target[kSecAttrAccessible as String] = kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
256
+ let added = SecItemAdd(target as CFDictionary, nil)
257
+ guard added == errSecSuccess || added == errSecDuplicateItem else { return false }
258
+
259
+ return SecItemDelete(source as CFDictionary) == errSecSuccess
356
260
  }
357
261
  ```
358
262
 
359
- > For the complete versioned migration approach and fresh-install detection pattern, see `migration-legacy-stores.md` section First-Launch Keychain Cleanup.
360
- > Key point: The pattern above handles the basic sharing-context case; the canonical file covers multi-version migration coordination, safe deletion ordering, and CI implications.
361
-
362
- ### App transfers between teams break keychain access
363
-
364
- Items are tied to the original Team ID. If an app is transferred to another developer account, keychain items stored under the old Team ID become inaccessible. Recommended workaround: transfer the app back, release an update that exports/migrates keychain data to an external store, then transfer again.
365
-
366
- ### Cross-developer sharing is impossible via access groups
367
-
368
- The Team ID prefix enforcement, through code-signed provisioning profiles, prevents apps from different teams from accessing each other's keychain items. Cross-developer credential sharing requires iCloud Keychain + Associated Domains (password autofill based on web domain ownership).
263
+ ## Lifecycle surprises
369
264
 
370
- ---
265
+ - Keychain items survive uninstalling the app. This is long-standing but
266
+ undocumented; an iOS 10.3 beta deleted them on removal and the change was reverted.
267
+ Apple DTS has warned it could change without notice.
268
+ - Deleting app A leaves group items in place for app B. Deleting every app in the group
269
+ still leaves the items as orphans; only erasing the device reliably removes them.
270
+ - A fresh install therefore can find old items. UserDefaults is removed on uninstall,
271
+ so a flag there tells a first launch apart:
371
272
 
372
- ## Platform-Specific Patterns
373
-
374
- ### watchOS
375
-
376
- watchOS 2+ runs a **separate keychain** not connected to the paired iPhone's keychain through access groups. Sharing credentials between iPhone and Watch requires either iCloud Keychain sync (`kSecAttrSynchronizable: true`, available since watchOS 6.2) or WatchConnectivity data transfer. For watchOS apps, add Keychain Sharing to the **WatchKit Extension target**, not the WatchKit App target.
377
-
378
- ### Widget Extensions (WidgetKit)
379
-
380
- Widget extensions follow the same rules as all app extensions - add Keychain Sharing or App Groups capabilities to the widget extension target independently. Widgets commonly need auth tokens for network requests. Store these in the shared keychain group rather than `UserDefaults(suiteName:)`, which lacks keychain-level encryption. App Group shared containers use only standard filesystem encryption (`NSFileProtectionCompleteUntilFirstUserAuthentication`), making the keychain the more secure choice for sensitive credentials.
381
-
382
- ---
383
-
384
- ## Build and Distribution Considerations
385
-
386
- The entitlement format and Team ID prefix rules are consistent across all build configurations: development, Ad Hoc, TestFlight, and App Store distribution. The Team ID is inherent to the developer account and does not change between configurations.
387
-
388
- However, the specific **provisioning profile** for each distribution type dictates which entitlements are allowed and embeds the correct `AppIdentifierPrefix`. Verify that the provisioning profile for each build type correctly authorizes the required access groups.
389
-
390
- **Legacy account caveat:** Most modern accounts use the Team ID as the App ID prefix, but legacy accounts (pre-June 2011) may have per-app prefixes that differ from the Team ID. Adding capabilities like Associated Domains to one target but not another has been reported to change the prefix, causing `-34018` errors. Ensure all targets sharing a keychain group have identical capabilities.
391
-
392
- ---
393
-
394
- ## Debugging When Keychain Sharing Breaks
395
-
396
- ### Essential Error Codes
397
-
398
- | Code | Constant | Meaning |
399
- | ---------- | ----------------------------- | ---------------------------------------------------------------- |
400
- | **0** | `errSecSuccess` | Operation succeeded |
401
- | **-25299** | `errSecDuplicateItem` | Item exists; use `SecItemUpdate` instead |
402
- | **-25300** | `errSecItemNotFound` | No match found; also returned pre-iOS 13 for unauthorized groups |
403
- | **-34018** | `errSecMissingEntitlement` | App lacks entitlement for the specified access group |
404
- | **-25308** | `errSecInteractionNotAllowed` | Device locked and item requires `WhenUnlocked` access |
405
- | **-50** | `errSecParam` | Invalid parameter (missing `kSecClass`, wrong value types) |
406
-
407
- Starting with **iOS 13**, querying an unauthorized access group returns the explicit `errSecMissingEntitlement` (-34018) instead of the ambiguous `errSecItemNotFound`. This makes debugging significantly easier on modern OS versions.
408
-
409
- ### Debugging Checklist
410
-
411
- **1. Verify entitlements on the built binary** - not the `.entitlements` source file:
412
-
413
- ```bash
414
- codesign -d --entitlements :- /path/to/YourApp.app
415
- codesign -d --entitlements :- /path/to/YourExtension.appex
416
- ```
417
-
418
- Compare the `keychain-access-groups` arrays - they must contain a common group.
419
-
420
- **2. Inspect the provisioning profile:**
421
-
422
- ```bash
423
- security cms -D -i YourApp.app/embedded.mobileprovision
273
+ ```swift
274
+ func purgeLeftoversOnFreshInstall() {
275
+ let marker = "vault.didLaunch"
276
+ guard !UserDefaults.standard.bool(forKey: marker) else { return }
277
+ let scoped: [String: Any] = [
278
+ kSecClass as String: kSecClassGenericPassword,
279
+ kSecAttrService as String: SharedVault.service,
280
+ kSecAttrAccessGroup as String: SharedVault.group
281
+ ]
282
+ SecItemDelete(scoped as CFDictionary)
283
+ UserDefaults.standard.set(true, forKey: marker)
284
+ }
424
285
  ```
425
286
 
426
- Verify that `keychain-access-groups`, `com.apple.security.application-groups`, and `com.apple.developer.team-identifier` are present and correct.
427
-
428
- **3. Test on a physical device.** The iOS Simulator does not use real provisioning profiles and may not surface entitlement issues. Keychain Sharing behavior in the Simulator can differ from device behavior.
429
-
430
- **4. Monitor system logs.** Open Console.app, select the connected device, filter for "keychain", and reproduce the issue. The system logs explicit messages when an entitlement check fails, identifying the missing group.
431
-
432
- **5. Check for App ID prefix mismatches** across all sharing targets - especially if any target has different capabilities enabled.
433
-
434
- ### Test Matrix
435
-
436
- | Scenario | Main App | Share Ext | Widget Ext | Expected |
437
- | ------------------------------------------ | :------: | :-------: | :--------: | ----------------------------------------- |
438
- | Write/read in `TeamID.com.example.shared` | Pass | Pass | Pass | All targets see same item |
439
- | Write/read in `group.com.example.appsuite` | Pass | Pass | Pass | Only when `kSecAttrAccessGroup` specified |
440
- | iCloud sync (non-`ThisDeviceOnly`) | Pass | N/A | N/A | Item appears on second device |
441
- | Missing entitlement in extension | N/A | Fail | N/A | `-34018` or `-25300` |
442
-
443
- ---
444
-
445
- ## Security Threat Model Notes
446
-
447
- - **End-to-end encryption:** Synchronized iCloud Keychain items are encrypted end-to-end; Apple cannot decrypt them.
448
- - **Malicious device risk:** A device joined to the user's iCloud account could potentially access or poison synchronized keychain items. Always scope secrets minimally and validate data retrieved from shared or synchronized keychains.
449
- - **Over-sharing risk:** Items placed in a shared access group are readable by all apps in that group. Use the narrowest possible access group - do not share an access group across apps that do not need the same credentials.
450
- - **Orphaned items:** After all apps in a shared group are uninstalled, keychain items remain on-device until factory reset. Consider this when storing highly sensitive data.
451
-
452
- ---
453
-
454
- ## What Changed in 2024-2026
455
-
456
- The core `SecItem` API has **not changed**. No new keychain-sharing-specific APIs were introduced in iOS 17, 18, or macOS 14/15. Apple still has not shipped a Swift-native keychain wrapper; the C-based Security framework remains the only official interface.
457
-
458
- The **Passwords app** introduced in iOS 18 and macOS Sequoia (WWDC 2024) provides a dedicated user-facing interface for managing passwords, passkeys, and verification codes. This is a UI layer over iCloud Keychain - it does not affect the `SecItem` API or access group mechanics.
459
-
460
- **Passkey enhancements** continued through WWDC 2024-2025, including automatic passkey upgrades and credential import/export APIs (`ASCredentialExportManager`). These operate at the credential-manager level and do not introduce new keychain-sharing mechanisms.
461
-
462
- `kSecAttrAccessibleAlways` and `kSecAttrAccessibleAlwaysThisDeviceOnly` remain deprecated since iOS 12. Use `kSecAttrAccessibleAfterFirstUnlock` or the more restrictive `kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly`.
463
-
464
- ---
465
-
466
- ## Cross-References
467
-
468
- - `keychain-fundamentals.md` - SecItem CRUD patterns, `kSecUseDataProtectionKeychain` on macOS, query dictionary construction
469
- - `keychain-access-control.md` - Accessibility constants for shared items, `ThisDeviceOnly` vs syncable implications
470
- - `keychain-item-classes.md` - Composite primary keys and how `kSecAttrAccessGroup` interacts with each `kSecClass`
471
- - `common-anti-patterns.md` - Anti-pattern #5 (missing `kSecAttrAccessible`), which compounds in shared contexts
472
- - `credential-storage-patterns.md` - OAuth token sharing between app and extensions
473
-
474
- ---
475
-
476
- ## Conclusion
477
-
478
- Keychain sharing on Apple platforms is a precise, entitlement-driven system where small configuration errors - a missing Team ID prefix, a capability not added to an extension target, a forgotten `kSecUseDataProtectionKeychain` on macOS - produce cryptic errors with no runtime warnings. The access group array's three-source concatenation order determines defaults and search scope in ways that catch developers off guard.
479
-
480
- Three rules prevent most issues: always include the full Team ID prefix in code (`TEAMID.com.example.shared`, never just `com.example.shared`); add Keychain Sharing to every executable target that needs access, not just the main app; and set `kSecUseDataProtectionKeychain` to `true` on macOS for iOS-consistent behavior. For iCloud sync, remember that `kSecAttrSynchronizable` defaults to `false` and that queries must explicitly opt in to find synchronizable items.
481
-
482
- ---
483
-
484
- ## Summary Checklist
485
-
486
- 1. **Team ID prefix in code** - Access group strings in Swift must use the fully resolved `TEAMID.com.example.shared` format; `$(AppIdentifierPrefix)` only works in entitlements plists.
487
- 2. **Per-target entitlements** - Every executable target (main app, each extension) must independently have the Keychain Sharing capability added in Xcode with the same group identifier.
488
- 3. **Keychain Sharing vs App Groups** - These are separate entitlements with different formats (`keychain-access-groups` with Team ID prefix vs `com.apple.security.application-groups` with `group.` prefix). App Groups cannot serve as keychain access groups on macOS.
489
- 4. **Default access group awareness** - The first entry in the concatenated access group array (keychain groups → app identifier → app groups) becomes the default. App Groups can never be the default.
490
- 5. **Explicit `kSecAttrAccessGroup`** - Always specify the access group in both `SecItemAdd` and `SecItemCopyMatching` calls. Omitting it on add uses the default group (which may be unexpected); omitting it on query searches all groups (which may be slow or overly broad).
491
- 6. **iCloud sync is opt-in** - `kSecAttrSynchronizable` defaults to `false`. Sync requires non-`ThisDeviceOnly` accessibility, and queries must include `kSecAttrSynchronizable: true` or `kSecAttrSynchronizableAny` to find synced items.
492
- 7. **macOS data protection keychain** - Set `kSecUseDataProtectionKeychain: true` on all macOS `SecItem` calls. Without it, `kSecAttrAccessGroup` is silently ignored and the legacy file-based keychain is used.
493
- 8. **Items persist after uninstall** - Keychain items survive app deletion. Use a `UserDefaults` flag to detect fresh installs and clean up stale items. Scope deletion carefully to avoid nuking shared items.
494
- 9. **`kSecAttrAccessGroup` is immutable** - Moving an item between groups requires a read-add-delete sequence, not an update.
495
- 10. **Verify built binary entitlements** - Use `codesign -d --entitlements :-` on the built `.app`/`.appex` to confirm entitlements, not the source `.entitlements` file. Test on physical devices; the Simulator may not surface entitlement issues.
496
- 11. **watchOS is isolated** - The Apple Watch has a separate keychain not connected via access groups. Use iCloud Keychain sync or WatchConnectivity for cross-device credential sharing.
287
+ Scope the delete by service and group; never wipe everything in a shared group,
288
+ because sibling apps may still be installed and using it. The complete launch
289
+ sequence is in [migration-legacy-stores.md](migration-legacy-stores.md).
290
+ - Transferring an app to another developer team orphans its items under the old Team
291
+ ID. The only workaround is to transfer back, ship an update that exports or migrates
292
+ the data somewhere else, then transfer again.
293
+ - Access groups cannot be shared with another developer's apps. Use iCloud Keychain
294
+ with Associated Domains for that.
295
+
296
+ ## Platform notes
297
+
298
+ - **watchOS.** Since watchOS 2 the watch has its own keychain, not joined to the
299
+ iPhone's through access groups. Share through iCloud Keychain sync
300
+ (`kSecAttrSynchronizable: true`, watchOS 6.2+) or send data over WatchConnectivity.
301
+ Add Keychain Sharing to the WatchKit extension target, not the WatchKit app target.
302
+ - **Widgets.** Put the capabilities on the widget target itself. Keep auth tokens in
303
+ the shared keychain group, not in `UserDefaults(suiteName:)`.
304
+ - **App Group containers** get only standard file protection
305
+ (`NSFileProtectionCompleteUntilFirstUserAuthentication`). The Keychain is the safer
306
+ home for credentials.
307
+
308
+ ## Signing and distribution
309
+
310
+ - The entitlement format and Team ID prefix are the same for development, Ad Hoc,
311
+ TestFlight and App Store builds.
312
+ - Each distribution profile decides which entitlements are allowed and embeds the
313
+ `AppIdentifierPrefix`. Check that every one of them authorizes the groups.
314
+ - Accounts created before June 2011 may have per-app prefixes that differ from the
315
+ Team ID. There are reports that adding a capability (Associated Domains, for example)
316
+ to one target but not another changes that target's prefix and produces -34018. Keep
317
+ capabilities identical across targets that share a group.
318
+
319
+ ## When sharing breaks
320
+
321
+ | Status | Meaning |
322
+ | --- | --- |
323
+ | 0 `errSecSuccess` | Worked |
324
+ | -25299 `errSecDuplicateItem` | Item exists; update it instead |
325
+ | -25300 `errSecItemNotFound` | No match; before iOS 13 also the result for a group you are not entitled to |
326
+ | -34018 `errSecMissingEntitlement` | The target is not entitled to that group (iOS 13+) |
327
+ | -25308 `errSecInteractionNotAllowed` | Device locked and the item is WhenUnlocked |
328
+ | -50 `errSecParam` | Bad query: missing `kSecClass`, wrong value types, or sync combined with ThisDeviceOnly |
329
+ | -128 `errSecUserCanceled` | The user dismissed an authentication prompt |
330
+
331
+ From iOS 13 an unauthorized group reports -34018 rather than -25300.
332
+
333
+ Steps:
334
+
335
+ 1. Inspect the entitlements actually signed into the build:
336
+ `codesign -d --entitlements :- MyApp.app` and the same for `MyWidget.appex`. The
337
+ arrays must share the group.
338
+ 2. Decode the embedded profile: `security cms -D -i MyApp.app/embedded.mobileprovision`.
339
+ Check `keychain-access-groups`, `com.apple.security.application-groups` and
340
+ `com.apple.developer.team-identifier`.
341
+ 3. Test on a physical device. The Simulator lacks real provisioning and can hide
342
+ entitlement problems.
343
+ 4. Watch the device in Console.app filtered on "keychain"; failed entitlement checks
344
+ name the group.
345
+ 5. Compare App ID prefixes across all targets.
346
+
347
+ | Test | Expected |
348
+ | --- | --- |
349
+ | Team-ID group item read by app, share extension, widget | All succeed |
350
+ | Item in a `group.` group | Works only with explicit `kSecAttrAccessGroup` |
351
+ | Synced item (not ThisDeviceOnly) | Appears on a second device on the same account |
352
+ | Extension without the entitlement | -34018 (or -25300 before iOS 13) |
353
+
354
+ ## Threat model
355
+
356
+ - Synced items are end-to-end encrypted.
357
+ - Any device the user adds to their iCloud account can read, and could tamper with,
358
+ synced items. Sync as little as possible and validate what you read back.
359
+ - Every app in a group can read every item in it. Use the narrowest group.
360
+ - Orphaned items persist until the device is erased; weigh that for very sensitive
361
+ data.
362
+
363
+ ## 2024 to 2026
364
+
365
+ - The `SecItem` API is unchanged. iOS 17/18 and macOS 14/15 added no sharing APIs, and
366
+ there is still no Apple Swift-native Keychain wrapper.
367
+ - The Passwords app in iOS 18 and macOS Sequoia is a front end to iCloud Keychain; it
368
+ changes no API or group behaviour.
369
+ - Passkey work (automatic upgrades, `ASCredentialExportManager` import and export)
370
+ happens at the credential-manager level and adds no sharing mechanism.
371
+ - The `Always` accessibility constants have been deprecated since iOS 12. Use
372
+ `AfterFirstUnlock` or `WhenPasscodeSetThisDeviceOnly`.
373
+
374
+ ## Related
375
+
376
+ [keychain-fundamentals.md](keychain-fundamentals.md) for the SecItem contract,
377
+ [keychain-access-control.md](keychain-access-control.md) for accessibility classes,
378
+ [keychain-item-classes.md](keychain-item-classes.md) for primary keys,
379
+ [common-anti-patterns.md](common-anti-patterns.md) (#5, wrong data protection class),
380
+ and [credential-storage-patterns.md](credential-storage-patterns.md) for sharing tokens
381
+ with extensions.
382
+
383
+ ## Checklist
384
+
385
+ - [ ] Code uses the full `TEAMID.` prefixed group string.
386
+ - [ ] Keychain Sharing is on every executable target.
387
+ - [ ] Keychain Sharing and App Groups formats are not mixed up; App Groups are not
388
+ used for Keychain on macOS.
389
+ - [ ] Default group understood: keychain groups, then app id, then app groups; an app
390
+ group is never the default.
391
+ - [ ] `kSecAttrAccessGroup` set explicitly on every add and query.
392
+ - [ ] Sync is opt-in, never with ThisDeviceOnly, and queries include
393
+ `kSecAttrSynchronizable: true` or `kSecAttrSynchronizableAny`.
394
+ - [ ] macOS calls set `kSecUseDataProtectionKeychain: true`.
395
+ - [ ] Leftover items after reinstall handled with a UserDefaults flag and a scoped
396
+ delete.
397
+ - [ ] Group changes done as read, add, then delete.
398
+ - [ ] Built entitlements checked with `codesign`; tested on a device.
399
+ - [ ] watchOS treated as a separate keychain; shared through sync or WatchConnectivity.