@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,90 +1,96 @@
1
1
  ---
2
2
  name: ios-accessibility
3
- description: "Implements, reviews, or improves accessibility in iOS/macOS apps with SwiftUI, UIKit, and AppKit. Use when adding VoiceOver, Voice Control, Switch Control, or Full Keyboard Access support; when working with accessibility labels, hints, values, traits, accessibilityInputLabels, NSAccessibility, grouping, reading order, accessibility focus restoration with @AccessibilityFocusState, Dynamic Type, @ScaledMetric, custom rotors, accessibility actions, XCTest accessibility checks, App Store Accessibility Nutrition Labels, App Store Connect accessibility answers, a11y compliance audits, or system accessibility preferences."
3
+ description: "VoiceOver, Voice Control, Switch Control and Full Keyboard Access in SwiftUI, UIKit and AppKit on iOS, iPadOS and macOS: labels, hints, values, traits, accessibilityInputLabels, NSAccessibility, grouping and reading order, @AccessibilityFocusState focus restore, Dynamic Type and @ScaledMetric, rotors, custom actions, XCTest accessibility checks, system accessibility preferences, Accessibility Nutrition Labels and App Store Connect answers. Use when implementing, reviewing or auditing accessibility (a11y). Not for keyboard or directional focus mechanics (Tab order, FocusState, UIFocusGuide): use hig-inputs."
4
4
  metadata:
5
- source: "dpearson2699/swift-ios-skills (PolyForm Perimeter 1.0.0)"
5
+ source: multi-agent-pipeline
6
6
  ---
7
7
 
8
- # iOS/macOS Accessibility - SwiftUI, UIKit, and AppKit
9
-
10
- Every user-facing view must be usable with VoiceOver, Switch Control, Voice Control, Full Keyboard Access, and other assistive technologies. This skill covers SwiftUI, UIKit, and AppKit patterns required to build accessible iOS, iPadOS, and macOS apps.
11
-
12
- ## Contents
13
-
14
- - [Core Principles](#core-principles)
15
- - [How VoiceOver Reads Elements](#how-voiceover-reads-elements)
16
- - [SwiftUI Accessibility Modifiers](#swiftui-accessibility-modifiers)
17
- - [Focus Management](#focus-management)
18
- - [Dynamic Type](#dynamic-type)
19
- - [Custom Rotors](#custom-rotors)
20
- - [System Accessibility Preferences](#system-accessibility-preferences)
21
- - [Decorative Content](#decorative-content)
22
- - [Voice Control](#voice-control)
23
- - [Switch Control](#switch-control)
24
- - [Full Keyboard Access](#full-keyboard-access)
25
- - [Assistive Access (iOS 18+)](#assistive-access-ios-18)
26
- - [UIKit Accessibility Patterns](#uikit-accessibility-patterns)
27
- - [AppKit Accessibility Patterns](#appkit-accessibility-patterns)
28
- - [Accessibility Custom Content](#accessibility-custom-content)
29
- - [App Store Accessibility Nutrition Labels](#app-store-accessibility-nutrition-labels)
30
- - [Testing Accessibility](#testing-accessibility)
31
- - [Common Mistakes](#common-mistakes)
32
- - [Review Checklist](#review-checklist)
33
- - [References](#references)
34
-
35
- ---
36
-
37
- ## Core Principles
38
-
39
- 1. Every interactive element MUST have an accessible label. If no visible text exists, add `.accessibilityLabel`.
40
- 2. Every custom control MUST have correct traits via `.accessibilityAddTraits` (never direct assignment). For binary custom controls such as favorite/star buttons, prefer a real `Toggle`; otherwise expose toggle behavior with `.accessibilityAddTraits(.isToggle)` and a current state value without putting the control type in the label.
41
- 3. Custom adjustable controls such as quantity steppers MUST expose adjustable behavior with `.accessibilityAdjustableAction`; UIKit custom adjustable controls also need the `.adjustable` trait.
42
- 4. Decorative images MUST be hidden from assistive technologies.
43
- 5. Sheet and dialog dismissals MUST return VoiceOver focus to the trigger element.
44
- 6. All tap targets MUST be at least 44x44 points.
45
- 7. Dynamic Type MUST be supported everywhere (system fonts, `@ScaledMetric`, adaptive layouts).
46
- 8. No information conveyed by color alone -- always provide text or icon alternatives.
47
- 9. System accessibility preferences MUST be respected: Reduce Motion, Reduce Transparency, Bold Text, Increase Contrast.
48
-
49
- ## How VoiceOver Reads Elements
50
-
51
- VoiceOver reads element properties in a fixed, non-configurable order:
52
-
53
- **Label -> Value -> Trait -> Hint**
54
-
55
- Design your labels, values, and hints with this reading order in mind.
56
-
57
- ## SwiftUI Accessibility Modifiers
58
-
59
- See [references/a11y-patterns.md](references/a11y-patterns.md) for detailed SwiftUI modifier examples (labels, hints, traits, grouping, custom controls, adjustable actions, and custom actions).
60
-
61
- ## Focus Management
62
-
63
- Focus management is where most apps fail. When a sheet, alert, or popover is dismissed, VoiceOver focus MUST return to the element that triggered it.
64
-
65
- This section is about accessibility focus for assistive technologies. For keyboard focus, directional focus, `focusSection()`, scene-focused values, and `UIFocusGuide`, use the `focus-engine` skill.
66
-
67
- When triaging broad focus bugs, still call out accessibility traversal separately: accessibility element order and grouping in the view hierarchy directly affect VoiceOver swipe order, Switch Control scan order, Voice Control overlay targeting, and Full Keyboard Access reachability review. Route keyboard-focus implementation to `focus-engine`, but keep this traversal impact in `ios-accessibility`.
8
+ # iOS Accessibility
9
+
10
+ Whatever appears on screen must also work for people who navigate with
11
+ VoiceOver, speak commands through Voice Control, scan with Switch Control, drive
12
+ a hardware keyboard through Full Keyboard Access, or rely on any other
13
+ assistive technology. Platforms: iOS, iPadOS and macOS. Frameworks: SwiftUI,
14
+ UIKit and AppKit.
15
+
16
+ References:
17
+
18
+ - [Code patterns](references/a11y-patterns.md): worked examples of the
19
+ modifiers in both UI frameworks, grouping, custom and adjustable controls, custom actions, rotors,
20
+ Dynamic Type, AppKit, Voice Control, Switch Control, Full Keyboard Access,
21
+ XCTest checks.
22
+ - [Nutrition labels](references/nutrition-labels.md): the current App Store
23
+ label categories and what passes or fails each one.
24
+ - [Media](references/media-accessibility.md): captions, SDH, audio
25
+ descriptions and `AVMediaCharacteristic`.
26
+
27
+ ## Ground rules
28
+
29
+ 1. Anything interactive has an accessible label. No visible text means you add
30
+ `.accessibilityLabel`.
31
+ 2. Custom controls gain traits with `.accessibilityAddTraits`. Never replace
32
+ the trait set wholesale.
33
+ 3. A two-state custom control (star, pin, favourite) should be a real
34
+ `Toggle`. If it cannot be, add `.accessibilityAddTraits(.isToggle)`
35
+ (iOS 17+) and report on/off as the value; the label never says what kind of
36
+ control it is.
37
+ 4. Custom adjustable controls, such as a quantity stepper, implement
38
+ `.accessibilityAdjustableAction`. In UIKit they also carry the `.adjustable`
39
+ trait.
40
+ 5. Purely decorative images are hidden from assistive technologies.
41
+ 6. When a sheet or dialog closes, VoiceOver focus goes back to whatever
42
+ opened it.
43
+ 7. Nothing tappable is smaller than 44x44 points.
44
+ 8. Dynamic Type works everywhere: system text styles, `@ScaledMetric`, layouts
45
+ that adapt.
46
+ 9. Color is never the only signal; pair it with text or an icon.
47
+ 10. Honour Increase Contrast, Bold Text, Reduce Transparency and Reduce Motion.
48
+
49
+ ## What VoiceOver says, and in which order
50
+
51
+ VoiceOver speaks an element's parts in a fixed order you cannot change:
52
+ **label, value, trait, hint**. Write each piece knowing what comes before and
53
+ after it, so the sentence as a whole makes sense ("Volume, 60 percent,
54
+ adjustable, swipe up or down to adjust").
55
+
56
+ Modifier examples for labels, hints, traits, grouping, custom and adjustable
57
+ controls and custom actions: [code patterns](references/a11y-patterns.md).
58
+
59
+ ## Accessibility focus
60
+
61
+ Focus is where apps most often break for assistive technology users. After a
62
+ sheet, alert or popover goes away, VoiceOver focus must land back on the
63
+ control that opened it.
64
+
65
+ Scope: this section is about the assistive-technology cursor. `UIFocusGuide`,
66
+ `focusSection()`, scene-focused values, and keyboard or directional focus in
67
+ general are implementation topics for the `hig-inputs` skill. Even when
68
+ triaging a broad focus bug, report accessibility traversal as its own finding:
69
+ the order and grouping of elements decide the path a VoiceOver swipe follows,
70
+ the sequence a switch scan visits, where Voice Control places its overlays and
71
+ whether Full Keyboard Access can get somewhere. The keyboard wiring goes to `hig-inputs`; the traversal impact
72
+ finding stays here.
68
73
 
69
74
  ### `@AccessibilityFocusState` (iOS 15+)
70
75
 
71
- `@AccessibilityFocusState` is a property wrapper that reads and writes the current accessibility focus. It works with `Bool` for single-target focus or an optional `Hashable` enum for multi-target focus.
76
+ A property wrapper you can read to learn where accessibility focus is and
77
+ write to move it. One target: bind a `Bool`. Several: bind an optional enum
78
+ that conforms to `Hashable`.
72
79
 
73
80
  ```swift
74
- struct ContentView: View {
75
- @State private var showSheet = false
76
- @AccessibilityFocusState private var focusOnTrigger: Bool
81
+ struct ReceiptRow: View {
82
+ @AccessibilityFocusState private var detailButtonFocused: Bool
83
+ @State private var showsDetail = false
77
84
 
78
85
  var body: some View {
79
- Button("Open Settings") { showSheet = true }
80
- .accessibilityFocused($focusOnTrigger)
81
- .sheet(isPresented: $showSheet) {
82
- SettingsSheet()
86
+ Button("Receipt details") { showsDetail = true }
87
+ .accessibilityFocused($detailButtonFocused)
88
+ .sheet(isPresented: $showsDetail) {
89
+ ReceiptDetail()
83
90
  .onDisappear {
84
- // Slight delay allows the transition to complete before moving focus
85
91
  Task { @MainActor in
86
- try? await Task.sleep(for: .milliseconds(100))
87
- focusOnTrigger = true
92
+ try? await Task.sleep(for: .seconds(0.1)) // about 100 ms
93
+ detailButtonFocused = true
88
94
  }
89
95
  }
90
96
  }
@@ -92,368 +98,390 @@ struct ContentView: View {
92
98
  }
93
99
  ```
94
100
 
95
- ### Multi-Target Focus with Enum
101
+ The short pause lets the dismissal animation finish before focus moves;
102
+ moving it mid-transition tends to be ignored. `Task.sleep(for:)` needs iOS 16;
103
+ on iOS 15 use `Task.sleep(nanoseconds: 100_000_000)`.
104
+
105
+ ### Several targets
96
106
 
97
107
  ```swift
98
- enum A11yFocus: Hashable {
99
- case nameField
100
- case emailField
101
- case submitButton
102
- }
108
+ enum SignUpField: Hashable { case handle, password, create }
103
109
 
104
- struct FormView: View {
105
- @AccessibilityFocusState private var focus: A11yFocus?
110
+ struct SignUpForm: View {
111
+ @State private var handle = ""
112
+ @AccessibilityFocusState private var a11yFocus: SignUpField?
106
113
 
107
114
  var body: some View {
108
115
  Form {
109
- TextField("Name", text: $name)
110
- .accessibilityFocused($focus, equals: .nameField)
111
- TextField("Email", text: $email)
112
- .accessibilityFocused($focus, equals: .emailField)
113
- Button("Submit") { validate() }
114
- .accessibilityFocused($focus, equals: .submitButton)
116
+ TextField("Handle", text: $handle)
117
+ .accessibilityFocused($a11yFocus, equals: .handle)
118
+ // ...same for the password field and the Create account button
115
119
  }
116
120
  }
117
121
 
118
- func validate() {
119
- if name.isEmpty {
120
- focus = .nameField // Move VoiceOver to the invalid field
121
- }
122
- }
122
+ private func submit() { if handle.isEmpty { a11yFocus = .handle } }
123
123
  }
124
124
  ```
125
125
 
126
- ### Custom Modals
126
+ When validation fails, assign the case of the offending field and VoiceOver
127
+ jumps straight to it.
128
+
129
+ ### Custom modals
127
130
 
128
- Custom overlay views need the `.isModal` trait to trap VoiceOver focus and an escape action for dismissal:
131
+ A hand-built overlay needs two things: the `.isModal` trait so VoiceOver
132
+ cannot wander outside it, and an escape action so the standard dismiss gesture
133
+ works.
129
134
 
130
135
  ```swift
131
- CustomDialog()
136
+ CouponPopover(onClose: close)
132
137
  .accessibilityAddTraits(.isModal)
133
- .accessibilityAction(.escape) { dismiss() }
138
+ .accessibilityAction(.escape) { close() }
134
139
  ```
135
140
 
136
- Test dismissal as part of the modal contract: users must be able to dismiss the overlay with the relevant assistive-technology escape gesture or keyboard escape path, and focus should return to the trigger or next logical target.
141
+ Closing is part of the modal's contract: test that the VoiceOver escape
142
+ gesture (two-finger scrub) or the keyboard Escape key dismisses it. Afterwards
143
+ focus belongs on the trigger or on the next sensible element.
137
144
 
138
- ### Accessibility Notifications (UIKit)
139
-
140
- When you need to announce changes or move focus imperatively in UIKit contexts:
145
+ ### UIKit notifications
141
146
 
142
147
  ```swift
143
- // Announce a status change (e.g., "Item deleted", "Upload complete")
144
- UIAccessibility.post(notification: .announcement, argument: "Upload complete")
145
-
146
- // Partial screen update -- move focus to a specific element
147
- UIAccessibility.post(notification: .layoutChanged, argument: targetView)
148
-
149
- // Full screen transition -- move focus to the new screen
150
- UIAccessibility.post(notification: .screenChanged, argument: newScreenView)
148
+ UIAccessibility.post(notification: .announcement, argument: "Draft saved")
149
+ UIAccessibility.post(notification: .layoutChanged, argument: errorBanner)
150
+ UIAccessibility.post(notification: .screenChanged, argument: summaryHeader)
151
151
  ```
152
152
 
153
- ## Dynamic Type
154
-
155
- Scale text with system text styles. Scale non-text dimensions too: icon sizes, spacing, control heights, and custom hit-region dimensions should use `@ScaledMetric(relativeTo:)` where they need to track text size.
153
+ - `.announcement`: speak a status change (an export finished, a row was
154
+ removed).
155
+ - `.layoutChanged`: part of the screen changed; focus moves to the argument.
156
+ - `.screenChanged`: a whole new screen; focus moves to the argument.
156
157
 
157
- See [references/a11y-patterns.md](references/a11y-patterns.md) for Dynamic Type and adaptive layout examples, including `@ScaledMetric` and minimum tap target patterns.
158
+ ## Dynamic Type
158
159
 
159
- ## Custom Rotors
160
+ Text scales when you use system text styles. Sizes that should grow with the
161
+ text (icons, spacing, control heights, custom hit regions) use
162
+ `@ScaledMetric(relativeTo:)`. Examples, including minimum tap targets:
163
+ [code patterns](references/a11y-patterns.md).
160
164
 
161
- Rotors let VoiceOver users quickly navigate to specific content types. Add custom rotors for content-heavy screens. See [references/a11y-patterns.md](references/a11y-patterns.md) for complete rotor examples.
165
+ ## Custom rotors
162
166
 
163
- ## System Accessibility Preferences
167
+ A rotor lets a VoiceOver user hop straight between items of one kind, such as
168
+ unread threads or headings. Add them to screens dense with content; see
169
+ [code patterns](references/a11y-patterns.md).
164
170
 
165
- Always respect these environment values:
171
+ ## System preferences
166
172
 
167
- ```swift
168
- @Environment(\.accessibilityReduceMotion) var reduceMotion
169
- @Environment(\.accessibilityReduceTransparency) var reduceTransparency
170
- @Environment(\.colorSchemeContrast) var contrast // .standard or .increased
171
- @Environment(\.legibilityWeight) var legibilityWeight // .regular or .bold
172
- ```
173
+ | Environment value | Type | Setting |
174
+ |---|---|---|
175
+ | `\.accessibilityReduceMotion` | `Bool` | Reduce Motion |
176
+ | `\.accessibilityReduceTransparency` | `Bool` | Reduce Transparency |
177
+ | `\.colorSchemeContrast` | `.standard` / `.increased` | Increase Contrast |
178
+ | `\.legibilityWeight` | `.regular` / `.bold` | Bold Text |
173
179
 
174
180
  ### Reduce Motion
175
181
 
176
- Replace movement-based animations with crossfades or no animation:
182
+ With Reduce Motion on, replace movement with a crossfade or with no animation.
177
183
 
178
184
  ```swift
179
- withAnimation(reduceMotion ? nil : .spring()) {
180
- showContent.toggle()
185
+ struct FilterDrawer: View {
186
+ @Environment(\.accessibilityReduceMotion) private var reduceMotion
187
+ @State private var isExpanded = false
188
+
189
+ var body: some View {
190
+ VStack {
191
+ Button("Filters") {
192
+ withAnimation(reduceMotion ? nil : .spring()) { isExpanded.toggle() }
193
+ }
194
+ if isExpanded {
195
+ PanelView()
196
+ .transition(reduceMotion ? .opacity : .move(edge: .bottom))
197
+ }
198
+ }
199
+ }
181
200
  }
182
- content.transition(reduceMotion ? .opacity : .slide)
183
201
  ```
184
202
 
185
- Review every moving transition, including row deletion, quantity changes, sheet or checkout presentation, and modal dismissal. Under Reduce Motion, replace slide, bounce, parallax, spring, and large spatial transitions with opacity changes, instant state changes, or no animation.
203
+ Go through every moving transition, not just the obvious one: row removal,
204
+ quantity changes, presenting a sheet or checkout, dismissing a modal. Slides,
205
+ bounces, parallax, springs and big spatial moves become fades, an immediate
206
+ jump to the end state, or no animation at all.
186
207
 
187
- ### Reduce Transparency, Increase Contrast, Bold Text
208
+ ### Transparency, contrast, bold text
188
209
 
189
210
  ```swift
190
- // Solid backgrounds when transparency is reduced
191
- .background(reduceTransparency ? Color(.systemBackground) : Color(.systemBackground).opacity(0.85))
211
+ @Environment(\.accessibilityReduceTransparency) private var reduceTransparency
212
+ @Environment(\.colorSchemeContrast) private var contrast
213
+ @Environment(\.legibilityWeight) private var legibilityWeight
192
214
 
193
- // Stronger colors when contrast is increased
194
- .foregroundStyle(contrast == .increased ? .primary : .secondary)
215
+ private let base = Color(uiColor: .systemBackground)
195
216
 
196
- // Bold weight when system bold text is enabled
197
- .fontWeight(legibilityWeight == .bold ? .bold : .regular)
217
+ var body: some View {
218
+ Text(caption)
219
+ .foregroundStyle(contrast == .increased ? .primary : .secondary)
220
+ .fontWeight(legibilityWeight == .bold ? .bold : .regular)
221
+ .background(reduceTransparency ? base : base.opacity(0.8))
222
+ }
198
223
  ```
199
224
 
200
- ## Decorative Content
225
+ ## Decorative versus meaningful images
201
226
 
202
227
  ```swift
203
- // Decorative images: hidden from VoiceOver
204
- Image(decorative: "background-pattern")
205
- Image("visual-divider").accessibilityHidden(true)
206
-
207
- // Icon next to text: Label handles this automatically
208
- Label("Settings", systemImage: "gear")
209
-
210
- // Icon-only buttons: MUST have an accessibility label
211
- Button(action: { }) {
212
- Image(systemName: "gear")
228
+ Image(decorative: "confetti") // never announced
229
+ Image("wave-divider").accessibilityHidden(true) // hide an existing image
230
+ Label("Downloads", systemImage: "arrow.down") // icon folded into the text
231
+ Button { openFilters() } label: {
232
+ Image(systemName: "slider.horizontal.3")
213
233
  }
214
- .accessibilityLabel("Settings")
234
+ .accessibilityLabel("Filters") // icon-only button needs a name
215
235
  ```
216
236
 
217
- Treat an image as decorative only when it adds no information beyond adjacent accessible text. If it communicates a product variant, state, chart point, user-generated content, or another distinguishing detail, provide a meaningful description instead of hiding it.
237
+ An image counts as decorative only when nearby accessible text already says
238
+ everything it says. If it shows a product variant, a state, a chart point,
239
+ something a user posted, or any other distinguishing detail, describe it
240
+ instead of hiding it.
218
241
 
219
242
  ## Voice Control
220
243
 
221
- Voice Control relies on accessibility labels to generate spoken tap targets. If a label is missing or unspeakable, Voice Control cannot target the element.
222
-
223
- - Every interactive element MUST have a speakable accessibility label (no emoji-only, no symbol-only).
224
- - Labels must be unique within the visible screen - duplicate labels force users to disambiguate with overlay numbers.
225
- - Treat `accessibilityInputLabels` as pre-freeze accessibility work for long, awkward, localized, acronym-heavy, or commonly shortened spoken labels; do not defer it as polish. Voice Control and Full Keyboard Access use these. List alternatives in descending order of importance.
226
- - Apply `accessibilityInputLabels` broadly to any visible target whose primary label is hard to say, including repeated row actions, quantity controls, account/settings links, media controls, and localized labels with acronyms or product names.
227
- - Test with Voice Control enabled: say "Show Names" and "Show Numbers" to verify all interactive elements are targetable.
228
- - For Voice Control reviews, verify both overlays: "Show Names" confirms speakable labels, and "Show Numbers" confirms every visible interactive target can still be reached when names are missing, duplicated, or awkward.
229
-
230
- See [references/a11y-patterns.md](references/a11y-patterns.md) for `accessibilityInputLabels` examples and speakable label guidelines.
244
+ Spoken commands find their target through its accessibility label: the label
245
+ is the name a person says to tap the element. An element with no label, or a label nobody can say, cannot be
246
+ targeted by voice.
247
+
248
+ - Every interactive element needs a label a person can say aloud. No labels
249
+ made only of emoji or symbols.
250
+ - Labels must differ among the elements on screen at the same time;
251
+ duplicates push users onto overlay numbers.
252
+ - Add `accessibilityInputLabels` before feature freeze, not as late polish,
253
+ wherever a label is long, clumsy, localized, full of acronyms, or usually
254
+ shortened when spoken. Both Full Keyboard Access and Voice Control consume
255
+ them. List the entries most important first.
256
+ - Good candidates: media controls, settings and account links, per-row
257
+ actions that repeat down a list, quantity steppers, localized labels that
258
+ contain product names or acronyms.
259
+ - Turn Voice Control on and say "Show Names", then "Show Numbers". The names
260
+ overlay proves labels can be spoken; the numbers overlay proves every
261
+ visible target stays reachable even if its name is missing, duplicated or
262
+ awkward. A review
263
+ checks both overlays.
264
+
265
+ Examples and speakable-label guidance: [code patterns](references/a11y-patterns.md).
231
266
 
232
267
  ## Switch Control
233
268
 
234
- Switch Control scans accessibility elements sequentially in reading order. Proper grouping and custom actions are critical for usability.
235
-
236
- - Group related content with `.accessibilityElement(children: .combine)` to reduce scan stops.
237
- - Every scan target should be meaningful and actionable. Decorative elements hidden from VoiceOver are also hidden from Switch Control.
238
- - Switch Control users cannot perform swipe-to-delete, long-press, or multi-finger gestures. Expose these interactions as `.accessibilityAction(named:)` custom actions instead - Switch Control presents them as a menu.
239
- - Custom controls with non-standard hit areas should ensure `accessibilityFrame` accurately reflects the tappable region (for point scanning mode).
269
+ Switch Control steps through accessibility elements one at a time in reading
270
+ order, so grouping and custom actions matter more here than anywhere else.
240
271
 
241
- See [references/a11y-patterns.md](references/a11y-patterns.md) for custom action and grouping examples.
272
+ - `.accessibilityElement(children: .combine)` merges related pieces and
273
+ removes scan stops. Each remaining stop should mean something and do
274
+ something.
275
+ - Anything hidden from VoiceOver as decorative is skipped by Switch Control
276
+ too.
277
+ - Multi-finger gestures, long presses and swipe-to-delete are out of reach
278
+ for switch users. Offer those operations as `.accessibilityAction(named:)`; Switch
279
+ Control lists them in a menu.
280
+ - In point scanning mode, a custom control whose tappable area is unusual
281
+ must report an `accessibilityFrame` that matches that area.
242
282
 
243
283
  ## Full Keyboard Access
244
284
 
245
- Full Keyboard Access (iOS/iPadOS 13.4+) lets users navigate and operate an app with a hardware keyboard.
246
-
247
- This skill covers the accessibility review surface: whether all controls are reachable, clearly labeled, visibly focused, and operable without touch. If the bug is Tab traversal, skipped custom cards, `.focusable()`, `@FocusState`, `focusSection()`, directional movement, scene-focused values, tvOS focus behavior, or `UIFocusGuide`, route implementation to the `focus-engine` skill first. Keep only the accessibility finding here.
248
-
249
- - Every interactive element can be reached and activated with the keyboard.
250
- - Traversal order is logical and does not trap focus.
251
- - Focus indicators remain visible at all contrast and text-size settings.
252
- - Gesture-only behavior has a keyboard-operable alternative.
253
- - App shortcuts do not override system-defined shortcuts such as Cmd+C, Cmd+V, or Cmd+Tab.
254
-
255
- See [references/a11y-patterns.md](references/a11y-patterns.md) for Full Keyboard Access audit checks.
256
-
257
- ## Traversal Order
258
-
259
- Explicitly assess how accessibility element order and grouping affect traversal outcomes: VoiceOver swipe order, Switch Control scan order, Voice Control overlay targeting, and Full Keyboard Access reachability review can all break when grouping/order differs from visual or task order. Missing labels, duplicate labels, excessive row children, hidden custom controls, or grouping that does not match the visual/task order can make traversal confusing across all of them. Keep implementation mechanics for keyboard or directional routing in `focus-engine`; keep the accessibility impact and ordering audit here.
285
+ Available since iOS and iPadOS 13.4, Full Keyboard Access lets people run the
286
+ app from a hardware keyboard. This skill owns the review: can everything be
287
+ reached, is it clearly labelled, is focus visible, does it work without touch.
288
+ `UIFocusGuide`, tvOS focus, scene-focused values, directional movement,
289
+ `focusSection()`, `@FocusState`, `.focusable()`, custom cards that Tab jumps
290
+ over, and Tab traversal bugs in general go to `hig-inputs` first; only the accessibility finding
291
+ stays here.
292
+
293
+ Audit:
294
+
295
+ - the keyboard alone can reach and trigger every interactive element;
296
+ - the traversal order makes sense and never traps focus;
297
+ - focus indicators stay visible at every contrast and text-size setting;
298
+ - anything that only works through a gesture has a keyboard route;
299
+ - the app's own shortcuts leave system ones (Cmd+C, Cmd+V, Cmd+Tab) alone.
300
+
301
+ ## Traversal order
302
+
303
+ Always judge the effect of ordering and grouping on four things at once: the
304
+ path of VoiceOver swipes, the steps of a switch scan, where Voice Control
305
+ overlays land, and what the keyboard can reach. All four go wrong together
306
+ when grouping or order disagrees with the visual layout or the task order. Usual causes:
307
+ missing labels, duplicate labels, rows exposing too many children, custom
308
+ controls hidden from accessibility, grouping that does not match what the eye
309
+ sees. The keyboard and directional routing mechanics are `hig-inputs` work;
310
+ the ordering audit and its accessibility impact are reported here.
260
311
 
261
312
  ## Assistive Access (iOS 18+)
262
313
 
263
- Assistive Access provides a simplified interface for users with cognitive disabilities. Apps should support this mode:
314
+ This pared-down system mode serves people with cognitive disabilities, and
315
+ apps should behave well inside it.
264
316
 
265
317
  ```swift
266
- // Check if Assistive Access is active (iOS 18+)
267
- @Environment(\.accessibilityAssistiveAccessEnabled) var isAssistiveAccessEnabled
318
+ struct LibraryScreen: View {
319
+ @Environment(\.accessibilityAssistiveAccessEnabled) private var assistiveAccess
268
320
 
269
- var body: some View {
270
- if isAssistiveAccessEnabled {
271
- SimplifiedContentView()
272
- } else {
273
- FullContentView()
321
+ var body: some View {
322
+ if assistiveAccess { SimpleLibraryView() } else { FullLibraryView() }
274
323
  }
275
324
  }
276
325
  ```
277
326
 
278
- Key guidelines:
279
- - Reduce visual complexity: fewer controls, larger tap targets, simpler navigation
280
- - Use clear, literal language for labels and instructions
281
- - Minimize the number of choices presented at once
282
- - Test with Assistive Access enabled in Settings > Accessibility > Assistive Access
327
+ - Less on screen: fewer controls, bigger targets, flatter navigation.
328
+ - Literal, plain wording in labels and instructions.
329
+ - Only a few choices at a time.
330
+ - Try it under Settings > Accessibility > Assistive Access.
283
331
 
284
- ## UIKit Accessibility Patterns
332
+ From iOS 26 an app can also declare `UISupportsAssistiveAccess` in its
333
+ Info.plist and provide a dedicated `AssistiveAccess` scene.
285
334
 
286
- When working with UIKit views:
335
+ ## UIKit
287
336
 
288
- - Set `isAccessibilityElement = true` on meaningful custom views.
289
- - Set `accessibilityLabel` on all interactive elements without visible text.
290
- - Use `.insert()` and `.remove()` for trait modification (not direct assignment).
291
- - Set `accessibilityViewIsModal = true` on custom overlay views to trap focus.
292
- - Post `.announcement` for transient status messages.
293
- - Post `.layoutChanged` with a target view for partial screen updates.
294
- - Post `.screenChanged` for full screen transitions.
337
+ - `isAccessibilityElement = true` on custom views that carry meaning.
338
+ - `accessibilityLabel` on every interactive element without visible text.
339
+ - Change traits with `.insert()` and `.remove()` instead of assigning:
340
+ `badgeView.accessibilityTraits.insert(.button)`,
341
+ `badgeView.accessibilityTraits.remove(.staticText)`.
342
+ - `accessibilityViewIsModal = true` on custom overlays to keep focus inside.
343
+ - Post `.announcement` for passing status, `.layoutChanged` with a target for
344
+ partial updates, `.screenChanged` for full transitions.
295
345
 
296
- ```swift
297
- // UIKit trait modification
298
- customButton.accessibilityTraits.insert(.button)
299
- customButton.accessibilityTraits.remove(.staticText)
346
+ ## AppKit
300
347
 
301
- // Modal overlay
302
- overlayView.accessibilityViewIsModal = true
303
- ```
348
+ AppKit accessibility rests on `NSAccessibilityProtocol` plus role-specific
349
+ protocols. Standard controls already publish most of what assistive apps need,
350
+ including notifications, so prefer them and customise only where the defaults
351
+ fall short.
304
352
 
305
- ## AppKit Accessibility Patterns
353
+ - A custom `NSView` adopts the right role behaviour and reports its role,
354
+ label, value and supported actions accurately.
355
+ - Items with no view behind them use `NSAccessibilityElement`.
356
+ - Post `NSAccessibility` notifications when a change must reach assistive
357
+ apps.
306
358
 
307
- AppKit accessibility uses `NSAccessibilityProtocol` and related role-specific protocols to describe accessible elements. Standard AppKit controls already provide much of this behavior; customize labels, values, roles, and actions only when the defaults are insufficient.
359
+ The full custom-view example (a toggle-like `NSView` returning role, label,
360
+ "On"/"Off" value and a press action, posting `.valueChanged` when its state
361
+ changes) and `NSAccessibilityElement` usage are in
362
+ [code patterns](references/a11y-patterns.md#appkit).
308
363
 
309
- - Prefer standard AppKit controls first - they already expose accessibility metadata and notifications.
310
- - For custom `NSView` subclasses, adopt the appropriate role-specific accessibility behavior and return the correct role, label, value, and actions.
311
- - Use `NSAccessibilityElement` for accessible items that are not backed by their own `NSView`.
312
- - Post `NSAccessibility` notifications when state changes need to be announced to assistive apps.
364
+ ## Custom content
313
365
 
314
- ```swift
315
- final class FavoriteToggleView: NSView {
316
- var isFavorite = false {
317
- didSet {
318
- NSAccessibility.post(element: self, notification: .valueChanged)
319
- }
320
- }
321
-
322
- override func isAccessibilityElement() -> Bool { true }
323
- override func accessibilityRole() -> NSAccessibility.Role? { .button }
324
- override func accessibilityLabel() -> String? { "Favorite" }
325
- override func accessibilityValue() -> Any? { isFavorite ? "On" : "Off" }
326
-
327
- override func accessibilityPerformPress() -> Bool {
328
- isFavorite.toggle()
329
- return true
330
- }
331
- }
332
- ```
333
-
334
- See [references/a11y-patterns.md](references/a11y-patterns.md) for AppKit examples including `NSAccessibilityElement` and announcement notifications.
335
-
336
- ## Accessibility Custom Content
337
-
338
- See [references/a11y-patterns.md](references/a11y-patterns.md) for UIKit and AppKit accessibility patterns and custom content examples.
366
+ `.accessibilityCustomContent` attaches extra labelled facts to an element.
367
+ By default VoiceOver offers them only on request (More Content rotor); with
368
+ `importance: .high` they are read along with the element.
339
369
 
340
370
  ```swift
341
- ProductRow(product: product)
342
- .accessibilityCustomContent("Price", product.formattedPrice)
343
- .accessibilityCustomContent("Rating", "\(product.rating) out of 5")
344
- .accessibilityCustomContent(
345
- "Availability",
346
- product.inStock ? "In stock" : "Out of stock",
347
- importance: .high // .high reads automatically with the element
348
- )
371
+ HotelCard(hotel: hotel)
372
+ .accessibilityCustomContent("Nightly rate", Text(hotel.rateText), importance: .high)
373
+ .accessibilityCustomContent("Guest score", Text(hotel.scoreText))
349
374
  ```
350
375
 
351
- ## App Store Accessibility Nutrition Labels
352
-
353
- For App Store accessibility nutrition labels, product-page claims, or App Store Connect accessibility answers, read [references/nutrition-labels.md](references/nutrition-labels.md).
354
-
355
- Before recommending a claim, require evidence that users can complete all common tasks with that feature on the relevant device type. Use a structured common-task by accessibility-feature matrix, include media transcripts when captions for audio-only content are relevant, and explicitly warn that App Store accessibility answers must stay accurate and must not be treated as marketing claims.
356
-
357
- ## Testing Accessibility
358
-
359
- ### Manual Testing
376
+ More UIKit, AppKit and custom-content examples:
377
+ [code patterns](references/a11y-patterns.md).
360
378
 
361
- - **Accessibility Inspector** (Xcode > Open Developer Tool): Audit views for missing labels, traits, and contrast issues. Run audits against the Simulator or connected device.
362
- - **VoiceOver testing**: Enable in Settings > Accessibility > VoiceOver. Navigate every screen with swipe gestures.
363
- - **Voice Control testing**: Enable in Settings > Accessibility > Voice Control. Say both "Show Names" and "Show Numbers"; names verify speakable labels, while numbers verify every visible interactive target is reachable even when names are duplicated, missing, or awkward.
364
- - **Full Keyboard Access testing**: Enable in Settings > Accessibility > Keyboards > Full Keyboard Access. Tab through every screen and verify all interactive elements receive focus.
365
- - **Switch Control testing**: Enable in Settings > Accessibility > Switch Control. Verify scan order is logical and custom actions appear for gesture-based interactions.
366
- - **Dynamic Type**: Test with all text sizes in Settings > Accessibility > Display & Text Size > Larger Text.
379
+ ## App Store Accessibility Nutrition Labels
367
380
 
368
- ### Automated Testing with XCTest
381
+ Questions about the labels, accessibility claims on the product page, or the
382
+ accessibility answers in App Store Connect: open the
383
+ [nutrition label reference](references/nutrition-labels.md) first. A claim is
384
+ only recommended after you:
369
385
 
370
- Use `XCUIElement` accessibility attributes to write UI tests that verify accessibility properties:
386
+ - ask for evidence that users can finish every common task with that feature
387
+ on the device type in question;
388
+ - lay the evidence out as a matrix of common tasks against features;
389
+ - include transcripts where audio-only content needs a text equivalent;
390
+ - say plainly that these answers must stay accurate and are not marketing.
371
391
 
372
- ```swift
373
- func testProductRowAccessibility() throws {
374
- let app = XCUIApplication()
375
- app.launch()
392
+ ## Testing
376
393
 
377
- let productCell = app.cells["product-organic-apples"]
378
- XCTAssertTrue(productCell.exists)
379
- XCTAssertTrue(productCell.isEnabled)
394
+ ### By hand
380
395
 
381
- // Verify the label is set and meaningful
382
- XCTAssertFalse(productCell.label.isEmpty)
396
+ | Tool | What to do | Where |
397
+ |---|---|---|
398
+ | Accessibility Inspector | Run an audit against the Simulator or a device; it reports contrast issues, absent labels and wrong traits | Xcode > Open Developer Tool |
399
+ | VoiceOver | Swipe from the first element to the last on each screen | Settings > Accessibility > VoiceOver |
400
+ | Voice Control | Say "Show Names", then "Show Numbers" | Settings > Accessibility > Voice Control |
401
+ | Full Keyboard Access | Press Tab across each screen and confirm each control receives focus | Settings > Accessibility > Keyboards > Full Keyboard Access |
402
+ | Switch Control | Watch the scan order; gesture features need custom actions | Settings > Accessibility > Switch Control |
403
+ | Dynamic Type | Step through the whole size range | Settings > Accessibility > Display & Text Size > Larger Text |
383
404
 
384
- // Verify a specific element has the expected label
385
- let favoriteButton = productCell.buttons["Favorite"]
386
- XCTAssertTrue(favoriteButton.exists)
387
- XCTAssertTrue(favoriteButton.isEnabled)
388
- }
389
- ```
390
-
391
- Key `XCUIElementAttributes` properties for accessibility verification: `label`, `identifier`, `value`, `isEnabled`, `hasFocus`, `isSelected`, `placeholderValue`, `title`.
405
+ ### With XCTest
392
406
 
393
- Test dismissal focus restoration:
407
+ UI tests can assert accessibility through `XCUIElement` attributes. The useful
408
+ ones in `XCUIElementAttributes` are `identifier`, `label`, `title`, `value`,
409
+ `placeholderValue`, `isSelected`, `isEnabled` and `hasFocus`.
394
410
 
395
411
  ```swift
396
- func testSheetDismissReturnsFocus() throws {
412
+ @MainActor
413
+ func testFocusReturnsAfterSheet() {
397
414
  let app = XCUIApplication()
398
415
  app.launch()
399
416
 
400
- let triggerButton = app.buttons["Open Settings"]
401
- triggerButton.tap()
402
-
403
- // Dismiss the sheet
404
- let doneButton = app.buttons["Done"]
405
- doneButton.tap()
406
-
407
- // Verify focus returns to trigger (in accessibility-focused testing)
408
- XCTAssertTrue(triggerButton.hasFocus)
417
+ let opener = app.buttons["Seat map"]
418
+ opener.tap()
419
+ app.buttons["Done"].tap()
420
+ XCTAssertTrue(opener.hasFocus)
409
421
  }
410
422
  ```
411
423
 
412
- ## Common Mistakes
413
-
414
- 1. **Direct trait assignment**: UIKit trait mutation or incorrect SwiftUI trait APIs can overwrite existing behavior. In SwiftUI, use `.accessibilityAddTraits(.isButton)`.
415
- 2. **Missing focus restoration**: Dismissing sheets without returning VoiceOver focus to the trigger element.
416
- 3. **Ungrouped list rows**: Multiple text elements per row create excessive swipe stops. Use `.accessibilityElement(children: .combine)`.
417
- 4. **Redundant trait in labels**: `.accessibilityLabel("Settings button")` reads as "Settings button, button." Omit the type.
418
- 5. **Missing labels on icon-only buttons**: Every `Image`-only button MUST have `.accessibilityLabel`.
419
- 6. **Ignoring Reduce Motion**: Always check `accessibilityReduceMotion` before movement animations.
420
- 7. **Fixed font sizes**: `.font(.system(size: 16))` ignores Dynamic Type. Use `.font(.body)` or similar text styles.
421
- 8. **Small tap targets**: Icons without `frame(minWidth: 44, minHeight: 44)` and `.contentShape()`.
422
- 9. **Color as sole indicator**: Red/green for error/success without text or icon alternatives.
423
- 10. **Missing `.isModal` on overlays**: Custom modals without `.accessibilityAddTraits(.isModal)` let VoiceOver escape.
424
-
425
- ## Review Checklist
426
-
427
- For every user-facing view, verify:
428
-
429
- - [ ] Every interactive element has an accessible label
430
- - [ ] Custom controls use correct traits via `.accessibilityAddTraits`
431
- - [ ] Adjustable custom controls expose adjustable behavior with `.accessibilityAdjustableAction` or UIKit `.adjustable`
432
- - [ ] Decorative images are hidden (`Image(decorative:)` or `.accessibilityHidden(true)`)
433
- - [ ] List rows group content with `.accessibilityElement(children: .combine)`
434
- - [ ] Sheets and dialogs return focus to the trigger on dismiss
435
- - [ ] Custom overlays have `.isModal` trait and escape action
436
- - [ ] All tap targets are at least 44x44 points
437
- - [ ] Dynamic Type supported (`@ScaledMetric`, system fonts, adaptive layouts)
438
- - [ ] Reduce Motion respected (no movement animations when enabled)
439
- - [ ] Row, checkout, sheet, and modal animations have Reduce Motion alternatives
440
- - [ ] Reduce Transparency respected (solid backgrounds when enabled)
441
- - [ ] Increase Contrast respected (stronger foreground colors)
442
- - [ ] No information conveyed by color alone
443
- - [ ] Custom actions provided for swipe-to-reveal and context menu features
444
- - [ ] Icon-only buttons have labels
445
- - [ ] Heading traits set on section headers
446
- - [ ] Custom accessibility types and notification payloads are `Sendable` when passed across concurrency boundaries
447
- - [ ] Labels are speakable and unique for Voice Control (no emoji-only or duplicate labels on screen)
448
- - [ ] Voice Control testing covers both "Show Names" and "Show Numbers"
449
- - [ ] `accessibilityInputLabels` provided for elements with long or awkward primary labels
450
- - [ ] Gesture-based interactions (swipe-to-delete, long-press) have accessibility custom action equivalents for Switch Control
451
- - [ ] Full Keyboard Access reaches and activates every control without focus traps
452
- - [ ] Element order and grouping are checked for traversal impact across VoiceOver, Switch Control, Voice Control overlays, and Full Keyboard Access review
453
- - [ ] System keyboard shortcuts are not overridden
454
-
455
- ## References
456
-
457
- - [references/a11y-patterns.md](references/a11y-patterns.md) - SwiftUI and UIKit modifier examples, grouping, custom actions, rotors, Dynamic Type
458
- - [references/nutrition-labels.md](references/nutrition-labels.md) - App Store Accessibility Nutrition Labels: current categories with pass/fail criteria
459
- - [references/media-accessibility.md](references/media-accessibility.md) - Captions, audio descriptions, AVMediaCharacteristic, SDH
424
+ A row test (querying `app.cells["id"]`, asserting `exists`, `isEnabled`, a
425
+ non-empty `label` and a nested `buttons[...]` query) is in
426
+ [code patterns](references/a11y-patterns.md#automated-checks-with-xctest).
427
+
428
+ `hasFocus` reports keyboard focus (Full Keyboard Access, hardware keyboard),
429
+ not the VoiceOver cursor, which XCTest cannot observe. Confirm VoiceOver focus
430
+ restoration by hand as well.
431
+
432
+ ## Mistakes seen most often
433
+
434
+ - Assigning traits (or using the wrong trait API) wipes existing behaviour;
435
+ use `.accessibilityAddTraits(.isButton)`.
436
+ - A sheet closes and VoiceOver focus is not put back on its trigger.
437
+ - Ungrouped list rows, one swipe stop per subview; combine them.
438
+ - The control type inside the label: `.accessibilityLabel("Filter button")`
439
+ is heard as "Filter button, button".
440
+ - `Image`-only buttons without `.accessibilityLabel`.
441
+ - Movement animations that never check `accessibilityReduceMotion`.
442
+ - Fixed sizes like `.font(.system(size: 15))` instead of `.font(.body)`.
443
+ - Small targets; fix with `.frame(minWidth: 44, minHeight: 44)` and
444
+ `.contentShape(Rectangle())`.
445
+ - Red versus green as the only error or success signal.
446
+ - A custom modal without `.accessibilityAddTraits(.isModal)`, so VoiceOver
447
+ wanders out of it.
448
+
449
+ ## Review checklist
450
+
451
+ - [ ] Every interactive element has a label.
452
+ - [ ] Custom controls get traits through `.accessibilityAddTraits`.
453
+ - [ ] Adjustable custom controls use `.accessibilityAdjustableAction`, or the
454
+ UIKit `.adjustable` trait.
455
+ - [ ] Decorative images are hidden (`Image(decorative:)` or
456
+ `.accessibilityHidden(true)`).
457
+ - [ ] Sheets and dialogs give focus back to their trigger.
458
+ - [ ] Each list row is a single combined element
459
+ (`.accessibilityElement(children: .combine)`).
460
+ - [ ] Custom overlays carry `.isModal` and an escape action.
461
+ - [ ] Every tap target is at least 44x44 pt.
462
+ - [ ] Dynamic Type works (`@ScaledMetric`, system fonts, adaptive layout).
463
+ - [ ] Reduce Motion removes movement animations.
464
+ - [ ] Each animated flow (sheet, modal, checkout, row changes) has a
465
+ reduced-motion version.
466
+ - [ ] Reduce Transparency gets solid backgrounds.
467
+ - [ ] Increase Contrast gets stronger foreground colors.
468
+ - [ ] Nothing is communicated by color alone.
469
+ - [ ] Context menus and swipe-revealed actions are also offered as custom
470
+ actions.
471
+ - [ ] Icon-only buttons are labelled.
472
+ - [ ] Section headers have the header trait.
473
+ - [ ] Any custom accessibility type or notification payload that crosses an
474
+ isolation boundary conforms to `Sendable`.
475
+ - [ ] Voice Control labels are speakable and unique (no emoji-only labels, no
476
+ duplicates on screen).
477
+ - [ ] Voice Control was exercised with the "Show Numbers" overlay as well as
478
+ "Show Names".
479
+ - [ ] Long or awkward labels have `accessibilityInputLabels`.
480
+ - [ ] Long-press and swipe-to-delete can also be done through custom actions,
481
+ for switch users.
482
+ - [ ] With Full Keyboard Access every control can be reached and triggered,
483
+ and focus never gets stuck.
484
+ - [ ] Ordering and grouping were reviewed for their effect on all four
485
+ traversal paths (VoiceOver, switch scanning, Voice Control overlays,
486
+ keyboard).
487
+ - [ ] The app leaves system keyboard shortcuts alone.