@mmerterden/multi-agent-pipeline 20.6.0 → 20.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/LICENSE +0 -10
- package/docs/facts.json +1 -1
- package/manifest.json +328 -329
- package/package.json +2 -2
- package/pipeline/multi-agent-refs/features/design-conformance.md +62 -64
- package/pipeline/scripts/_notices.mjs +12 -1
- package/pipeline/scripts/gen-skills-index.mjs +1 -1
- package/pipeline/skills/.skill-manifest.json +106 -106
- package/pipeline/skills/shared/README.md +71 -71
- package/pipeline/skills/shared/external/agent-introspection-debugging/SKILL.md +1 -0
- package/pipeline/skills/shared/external/alarmkit/SKILL.md +373 -381
- package/pipeline/skills/shared/external/alarmkit/evals/evals.json +23 -18
- package/pipeline/skills/shared/external/alarmkit/references/alarmkit-patterns.md +328 -378
- package/pipeline/skills/shared/external/android-architecture/SKILL.md +2 -0
- package/pipeline/skills/shared/external/android-performance/SKILL.md +2 -0
- package/pipeline/skills/shared/external/android-security/SKILL.md +2 -0
- package/pipeline/skills/shared/external/app-clips/SKILL.md +260 -160
- package/pipeline/skills/shared/external/app-clips/evals/evals.json +27 -27
- package/pipeline/skills/shared/external/app-clips/references/data-handoff-notifications-location.md +150 -83
- package/pipeline/skills/shared/external/app-clips/references/routing-and-experiences.md +135 -83
- package/pipeline/skills/shared/external/app-clips/references/size-capabilities-and-promotion.md +143 -85
- package/pipeline/skills/shared/external/app-intents/SKILL.md +302 -304
- package/pipeline/skills/shared/external/app-intents/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +594 -894
- package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +339 -277
- package/pipeline/skills/shared/external/app-store-optimization/evals/evals.json +27 -23
- package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +105 -122
- package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +143 -166
- package/pipeline/skills/shared/external/app-store-review/SKILL.md +307 -326
- package/pipeline/skills/shared/external/app-store-review/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +105 -67
- package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +114 -101
- package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +333 -360
- package/pipeline/skills/shared/external/apple-on-device-ai/evals/evals.json +24 -27
- package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +215 -322
- package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +161 -256
- package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +277 -387
- package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +196 -210
- package/pipeline/skills/shared/external/authentication/SKILL.md +265 -381
- package/pipeline/skills/shared/external/authentication/evals/evals.json +25 -25
- package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +133 -178
- package/pipeline/skills/shared/external/authentication/references/passkeys.md +111 -147
- package/pipeline/skills/shared/external/avkit/SKILL.md +267 -364
- package/pipeline/skills/shared/external/avkit/evals/evals.json +26 -26
- package/pipeline/skills/shared/external/avkit/references/avkit-patterns.md +375 -493
- package/pipeline/skills/shared/external/background-processing/SKILL.md +270 -382
- package/pipeline/skills/shared/external/background-processing/evals/evals.json +22 -22
- package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +169 -317
- package/pipeline/skills/shared/external/backlog/BACKLOG.md +1 -1
- package/pipeline/skills/shared/external/backlog/SKILL.md +56 -33
- package/pipeline/skills/shared/external/callkit-voip/SKILL.md +290 -371
- package/pipeline/skills/shared/external/callkit-voip/evals/evals.json +24 -24
- package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +175 -343
- package/pipeline/skills/shared/external/ci-cd-pipelines/SKILL.md +1 -0
- package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +292 -381
- package/pipeline/skills/shared/external/cloudkit-sync/evals/evals.json +33 -30
- package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +227 -355
- package/pipeline/skills/shared/external/compose-components/SKILL.md +2 -0
- package/pipeline/skills/shared/external/compose-navigation/SKILL.md +3 -2
- package/pipeline/skills/shared/external/compose-testing/SKILL.md +2 -0
- package/pipeline/skills/shared/external/contacts-framework/SKILL.md +197 -346
- package/pipeline/skills/shared/external/contacts-framework/evals/evals.json +19 -21
- package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +169 -308
- package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +226 -376
- package/pipeline/skills/shared/external/core-bluetooth/evals/evals.json +25 -22
- package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +257 -337
- package/pipeline/skills/shared/external/core-data/SKILL.md +292 -368
- package/pipeline/skills/shared/external/core-data/evals/evals.json +30 -27
- package/pipeline/skills/shared/external/core-motion/SKILL.md +235 -324
- package/pipeline/skills/shared/external/core-motion/evals/evals.json +31 -27
- package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +210 -310
- package/pipeline/skills/shared/external/core-nfc/SKILL.md +292 -366
- package/pipeline/skills/shared/external/core-nfc/evals/evals.json +22 -24
- package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +233 -329
- package/pipeline/skills/shared/external/coreml/SKILL.md +323 -367
- package/pipeline/skills/shared/external/coreml/evals/evals.json +24 -21
- package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +562 -565
- package/pipeline/skills/shared/external/council/SKILL.md +1 -0
- package/pipeline/skills/shared/external/cryptokit/SKILL.md +253 -394
- package/pipeline/skills/shared/external/cryptokit/evals/evals.json +20 -18
- package/pipeline/skills/shared/external/cryptokit/references/cryptokit-patterns.md +299 -488
- package/pipeline/skills/shared/external/css-modern/SKILL.md +1 -0
- package/pipeline/skills/shared/external/database-patterns/SKILL.md +1 -0
- package/pipeline/skills/shared/external/debugging-instruments/SKILL.md +270 -323
- package/pipeline/skills/shared/external/debugging-instruments/evals/evals.json +27 -30
- package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +167 -315
- package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +140 -193
- package/pipeline/skills/shared/external/device-integrity/SKILL.md +230 -353
- package/pipeline/skills/shared/external/device-integrity/evals/evals.json +25 -21
- package/pipeline/skills/shared/external/device-integrity/references/device-integrity-patterns.md +159 -197
- package/pipeline/skills/shared/external/energykit/SKILL.md +225 -392
- package/pipeline/skills/shared/external/energykit/evals/evals.json +29 -28
- package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +174 -470
- package/pipeline/skills/shared/external/eventkit-calendar/SKILL.md +261 -383
- package/pipeline/skills/shared/external/eventkit-calendar/evals/evals.json +25 -22
- package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +165 -268
- package/pipeline/skills/shared/external/evidence-github/SKILL.md +2 -0
- package/pipeline/skills/shared/external/evidence-registry/SKILL.md +2 -0
- package/pipeline/skills/shared/external/gradle-kotlin-dsl/SKILL.md +2 -0
- package/pipeline/skills/shared/external/healthkit/SKILL.md +252 -303
- package/pipeline/skills/shared/external/healthkit/evals/evals.json +24 -23
- package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +369 -523
- package/pipeline/skills/shared/external/homekit-matter/SKILL.md +233 -348
- package/pipeline/skills/shared/external/homekit-matter/evals/evals.json +27 -22
- package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +199 -305
- package/pipeline/skills/shared/external/html-semantic/SKILL.md +1 -0
- package/pipeline/skills/shared/external/humanizer/SKILL.md +1 -0
- package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +368 -340
- package/pipeline/skills/shared/external/ios-accessibility/evals/evals.json +28 -27
- package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +314 -260
- package/pipeline/skills/shared/external/ios-accessibility/references/media-accessibility.md +97 -67
- package/pipeline/skills/shared/external/ios-accessibility/references/nutrition-labels.md +165 -101
- package/pipeline/skills/shared/external/ios-coding-standard/SKILL.md +1 -0
- package/pipeline/skills/shared/external/ios-localization/SKILL.md +258 -371
- package/pipeline/skills/shared/external/ios-localization/evals/evals.json +23 -23
- package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +283 -491
- package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +313 -440
- package/pipeline/skills/shared/external/ios-module-structure/SKILL.md +1 -0
- package/pipeline/skills/shared/external/ios-networking/SKILL.md +265 -341
- package/pipeline/skills/shared/external/ios-networking/evals/evals.json +24 -24
- package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +425 -652
- package/pipeline/skills/shared/external/ios-networking/references/file-storage-patterns.md +143 -285
- package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +93 -53
- package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +231 -456
- package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +517 -784
- package/pipeline/skills/shared/external/ios-security/SKILL.md +2 -0
- package/pipeline/skills/shared/external/ios-simulator/SKILL.md +265 -393
- package/pipeline/skills/shared/external/ios-simulator/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/ios-simulator/references/simctl-commands.md +177 -270
- package/pipeline/skills/shared/external/live-activities/SKILL.md +318 -360
- package/pipeline/skills/shared/external/live-activities/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/live-activities/references/activitykit-patterns.md +478 -710
- package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +91 -283
- package/pipeline/skills/shared/external/localization-reuse-map/reference/format-and-output.md +119 -151
- package/pipeline/skills/shared/external/localization-reuse-map/reference/publish-and-snapshot.md +60 -90
- package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md +119 -156
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-artifact.py +726 -787
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-spreadsheet.py +253 -288
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-annotations.py +243 -304
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-legacy-labels.py +88 -104
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/publish-confluence.py +181 -235
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-key-shots.py +198 -263
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-overlay.py +461 -466
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-legacy-values.py +145 -151
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-new-values.py +123 -141
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/scan-screen-keys.py +146 -157
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/snapshot-resources.sh +22 -19
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/verify-map.py +156 -140
- package/pipeline/skills/shared/external/mapkit-location/SKILL.md +295 -267
- package/pipeline/skills/shared/external/mapkit-location/evals/evals.json +28 -24
- package/pipeline/skills/shared/external/mapkit-location/references/mapkit-corelocation-patterns.md +378 -532
- package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +397 -499
- package/pipeline/skills/shared/external/metrickit-diagnostics/SKILL.md +165 -348
- package/pipeline/skills/shared/external/metrickit-diagnostics/evals/evals.json +26 -23
- package/pipeline/skills/shared/external/metrickit-diagnostics/references/metrickit-patterns.md +123 -130
- package/pipeline/skills/shared/external/musickit-audio/SKILL.md +189 -315
- package/pipeline/skills/shared/external/musickit-audio/evals/evals.json +22 -21
- package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +181 -270
- package/pipeline/skills/shared/external/natural-language/SKILL.md +188 -340
- package/pipeline/skills/shared/external/natural-language/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +171 -225
- package/pipeline/skills/shared/external/nextjs-app-router/SKILL.md +1 -0
- package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +258 -392
- package/pipeline/skills/shared/external/passkit-wallet/evals/evals.json +30 -29
- package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +164 -231
- package/pipeline/skills/shared/external/pdfkit/SKILL.md +312 -344
- package/pipeline/skills/shared/external/pdfkit/evals/evals.json +19 -19
- package/pipeline/skills/shared/external/pdfkit/references/pdfkit-patterns.md +413 -624
- package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +242 -358
- package/pipeline/skills/shared/external/pencilkit-drawing/evals/evals.json +25 -21
- package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +161 -226
- package/pipeline/skills/shared/external/permissionkit/SKILL.md +282 -400
- package/pipeline/skills/shared/external/permissionkit/evals/evals.json +27 -30
- package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +237 -350
- package/pipeline/skills/shared/external/photos-camera-media/SKILL.md +276 -325
- package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +299 -545
- package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +344 -588
- package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +316 -660
- package/pipeline/skills/shared/external/photos-camera-media/references/photokit-patterns.md +270 -416
- package/pipeline/skills/shared/external/play-store-review/SKILL.md +2 -0
- package/pipeline/skills/shared/external/push-notifications/SKILL.md +312 -340
- package/pipeline/skills/shared/external/push-notifications/evals/evals.json +27 -26
- package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +328 -485
- package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +327 -560
- package/pipeline/skills/shared/external/python-patterns/SKILL.md +1 -0
- package/pipeline/skills/shared/external/react-best-practices/SKILL.md +1 -0
- package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +218 -410
- package/pipeline/skills/shared/external/realitykit-ar/evals/evals.json +24 -27
- package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +221 -348
- package/pipeline/skills/shared/external/rest-api-design/SKILL.md +1 -0
- package/pipeline/skills/shared/external/retrofit-networking/SKILL.md +2 -0
- package/pipeline/skills/shared/external/room-database/SKILL.md +2 -0
- package/pipeline/skills/shared/external/search-first/SKILL.md +1 -0
- package/pipeline/skills/shared/external/shareplay-activities/SKILL.md +222 -393
- package/pipeline/skills/shared/external/shareplay-activities/evals/evals.json +23 -24
- package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +280 -420
- package/pipeline/skills/shared/external/signal-community/SKILL.md +2 -0
- package/pipeline/skills/shared/external/skill-creator/SKILL.md +80 -41
- package/pipeline/skills/shared/external/skill-creator/audit.md +63 -59
- package/pipeline/skills/shared/external/skill-creator/checklist.md +28 -20
- package/pipeline/skills/shared/external/skill-creator/examples.md +40 -40
- package/pipeline/skills/shared/external/skill-creator/label-check.md +48 -36
- package/pipeline/skills/shared/external/skill-creator/scripts/audit-panel.js +91 -100
- package/pipeline/skills/shared/external/skill-creator/template.md +51 -39
- package/pipeline/skills/shared/external/speech-recognition/SKILL.md +217 -421
- package/pipeline/skills/shared/external/speech-recognition/evals/evals.json +23 -26
- package/pipeline/skills/shared/external/speech-recognition/references/speechanalyzer-patterns.md +133 -125
- package/pipeline/skills/shared/external/storekit/SKILL.md +228 -204
- package/pipeline/skills/shared/external/storekit/evals/evals.json +27 -24
- package/pipeline/skills/shared/external/storekit/references/app-review-guidelines.md +98 -109
- package/pipeline/skills/shared/external/storekit/references/core-patterns.md +298 -242
- package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +356 -649
- package/pipeline/skills/shared/external/swift-api-design-guidelines/SKILL.md +274 -399
- package/pipeline/skills/shared/external/swift-api-design-guidelines/evals/evals.json +22 -24
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/argument-labels-and-parameters.md +107 -108
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md +93 -165
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md +99 -137
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md +77 -120
- package/pipeline/skills/shared/external/swift-architecture/SKILL.md +334 -350
- package/pipeline/skills/shared/external/swift-architecture/evals/evals.json +22 -22
- package/pipeline/skills/shared/external/swift-charts/SKILL.md +208 -394
- package/pipeline/skills/shared/external/swift-charts/evals/evals.json +27 -30
- package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +351 -762
- package/pipeline/skills/shared/external/swift-codable/SKILL.md +339 -343
- package/pipeline/skills/shared/external/swift-codable/evals/evals.json +20 -20
- package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +303 -351
- package/pipeline/skills/shared/external/swift-concurrency/evals/evals.json +27 -24
- package/pipeline/skills/shared/external/swift-concurrency/references/approachable-concurrency.md +65 -80
- package/pipeline/skills/shared/external/swift-concurrency/references/async-algorithms.md +48 -84
- package/pipeline/skills/shared/external/swift-concurrency/references/bridging-interop.md +134 -79
- package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +145 -167
- package/pipeline/skills/shared/external/swift-concurrency/references/diagnostics.md +62 -50
- package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +92 -121
- package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +177 -241
- package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +258 -234
- package/pipeline/skills/shared/external/swift-language/SKILL.md +342 -382
- package/pipeline/skills/shared/external/swift-language/evals/evals.json +24 -27
- package/pipeline/skills/shared/external/swift-language/references/swift-attributes-interop.md +79 -56
- package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +297 -340
- package/pipeline/skills/shared/external/swift-security/SKILL.md +180 -161
- package/pipeline/skills/shared/external/swift-security/evals/evals.json +25 -25
- package/pipeline/skills/shared/external/swift-security/references/biometric-authentication.md +314 -469
- package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +408 -476
- package/pipeline/skills/shared/external/swift-security/references/common-anti-patterns.md +260 -530
- package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md +270 -477
- package/pipeline/skills/shared/external/swift-security/references/credential-storage-patterns.md +573 -571
- package/pipeline/skills/shared/external/swift-security/references/cryptokit-public-key.md +370 -441
- package/pipeline/skills/shared/external/swift-security/references/cryptokit-symmetric.md +332 -433
- package/pipeline/skills/shared/external/swift-security/references/keychain-access-control.md +346 -468
- package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +352 -472
- package/pipeline/skills/shared/external/swift-security/references/keychain-item-classes.md +431 -432
- package/pipeline/skills/shared/external/swift-security/references/keychain-sharing.md +328 -425
- package/pipeline/skills/shared/external/swift-security/references/migration-legacy-stores.md +341 -579
- package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +396 -457
- package/pipeline/skills/shared/external/swift-security/references/testing-security-code.md +354 -614
- package/pipeline/skills/shared/external/swift-testing/SKILL.md +188 -175
- package/pipeline/skills/shared/external/swift-testing/evals/evals.json +26 -24
- package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +80 -84
- package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +317 -433
- package/pipeline/skills/shared/external/swiftdata/SKILL.md +392 -256
- package/pipeline/skills/shared/external/swiftdata/evals/evals.json +24 -24
- package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +206 -402
- package/pipeline/skills/shared/external/swiftdata/references/indexing.md +59 -52
- package/pipeline/skills/shared/external/swiftdata/references/predicate-pitfalls.md +57 -33
- package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +354 -747
- package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +300 -508
- package/pipeline/skills/shared/external/swiftlint/SKILL.md +175 -226
- package/pipeline/skills/shared/external/swiftlint/references/adoption-and-configuration.md +141 -208
- package/pipeline/skills/shared/external/swiftlint/references/custom-rules-and-analyze.md +100 -109
- package/pipeline/skills/shared/external/swiftlint/references/plugins-run-scripts-and-integrations.md +159 -179
- package/pipeline/skills/shared/external/swiftlint/references/rule-reference.md +383 -18
- package/pipeline/skills/shared/external/swiftlint/references/rules-suppressions-and-baselines.md +143 -229
- package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +283 -366
- package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +396 -608
- package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +336 -385
- package/pipeline/skills/shared/external/swiftui-gestures/SKILL.md +239 -349
- package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +228 -310
- package/pipeline/skills/shared/external/swiftui-layout-components/SKILL.md +260 -249
- package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +92 -74
- package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +112 -177
- package/pipeline/skills/shared/external/swiftui-layout-components/references/list.md +61 -64
- package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +94 -134
- package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +193 -225
- package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +173 -327
- package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +193 -168
- package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +127 -150
- package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +132 -133
- package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +152 -117
- package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +106 -140
- package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +316 -252
- package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +341 -332
- package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +547 -854
- package/pipeline/skills/shared/external/swiftui-patterns/references/design-polish.md +485 -537
- package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +417 -499
- package/pipeline/skills/shared/external/swiftui-performance/SKILL.md +213 -376
- package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +86 -175
- package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +89 -195
- package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +95 -182
- package/pipeline/skills/shared/external/swiftui-performance/references/understanding-improving-swiftui-performance.md +71 -149
- package/pipeline/skills/shared/external/swiftui-performance/references/wwdc-session-sources.md +21 -27
- package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +303 -295
- package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +204 -387
- package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +469 -683
- package/pipeline/skills/shared/external/swiftui-webkit/SKILL.md +140 -186
- package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +75 -86
- package/pipeline/skills/shared/external/swiftui-webkit/references/local-content-and-custom-schemes.md +63 -60
- package/pipeline/skills/shared/external/swiftui-webkit/references/migration-and-fallbacks.md +69 -137
- package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +95 -67
- package/pipeline/skills/shared/external/tailwind-css/SKILL.md +1 -0
- package/pipeline/skills/shared/external/testing-backend/SKILL.md +1 -0
- package/pipeline/skills/shared/external/tipkit/SKILL.md +220 -335
- package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +356 -494
- package/pipeline/skills/shared/external/typescript-patterns/SKILL.md +1 -0
- package/pipeline/skills/shared/external/vision-framework/SKILL.md +260 -375
- package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +393 -515
- package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +363 -539
- package/pipeline/skills/shared/external/vue-composition/SKILL.md +1 -0
- package/pipeline/skills/shared/external/weatherkit/SKILL.md +152 -310
- package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +288 -407
- package/pipeline/skills/shared/external/web-accessibility/SKILL.md +1 -0
- package/pipeline/skills/shared/external/web-performance/SKILL.md +1 -0
- package/pipeline/skills/shared/external/web-testing/SKILL.md +1 -0
- package/pipeline/skills/shared/external/widgetkit/SKILL.md +216 -288
- package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +414 -719
- 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: "
|
|
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:
|
|
5
|
+
source: multi-agent-pipeline
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
# iOS
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
- [
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
- [
|
|
23
|
-
|
|
24
|
-
- [
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
##
|
|
50
|
-
|
|
51
|
-
VoiceOver
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
|
|
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
|
|
75
|
-
@
|
|
76
|
-
@
|
|
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("
|
|
80
|
-
.accessibilityFocused($
|
|
81
|
-
.sheet(isPresented: $
|
|
82
|
-
|
|
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: .
|
|
87
|
-
|
|
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
|
-
|
|
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
|
|
99
|
-
case nameField
|
|
100
|
-
case emailField
|
|
101
|
-
case submitButton
|
|
102
|
-
}
|
|
108
|
+
enum SignUpField: Hashable { case handle, password, create }
|
|
103
109
|
|
|
104
|
-
struct
|
|
105
|
-
@
|
|
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("
|
|
110
|
-
.accessibilityFocused($
|
|
111
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
136
|
+
CouponPopover(onClose: close)
|
|
132
137
|
.accessibilityAddTraits(.isModal)
|
|
133
|
-
.accessibilityAction(.escape) {
|
|
138
|
+
.accessibilityAction(.escape) { close() }
|
|
134
139
|
```
|
|
135
140
|
|
|
136
|
-
|
|
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
|
-
###
|
|
139
|
-
|
|
140
|
-
When you need to announce changes or move focus imperatively in UIKit contexts:
|
|
145
|
+
### UIKit notifications
|
|
141
146
|
|
|
142
147
|
```swift
|
|
143
|
-
|
|
144
|
-
UIAccessibility.post(notification: .
|
|
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
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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
|
-
|
|
158
|
+
## Dynamic Type
|
|
158
159
|
|
|
159
|
-
|
|
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
|
-
|
|
165
|
+
## Custom rotors
|
|
162
166
|
|
|
163
|
-
|
|
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
|
-
|
|
171
|
+
## System preferences
|
|
166
172
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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
|
-
|
|
182
|
+
With Reduce Motion on, replace movement with a crossfade or with no animation.
|
|
177
183
|
|
|
178
184
|
```swift
|
|
179
|
-
|
|
180
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
208
|
+
### Transparency, contrast, bold text
|
|
188
209
|
|
|
189
210
|
```swift
|
|
190
|
-
|
|
191
|
-
|
|
211
|
+
@Environment(\.accessibilityReduceTransparency) private var reduceTransparency
|
|
212
|
+
@Environment(\.colorSchemeContrast) private var contrast
|
|
213
|
+
@Environment(\.legibilityWeight) private var legibilityWeight
|
|
192
214
|
|
|
193
|
-
|
|
194
|
-
.foregroundStyle(contrast == .increased ? .primary : .secondary)
|
|
215
|
+
private let base = Color(uiColor: .systemBackground)
|
|
195
216
|
|
|
196
|
-
|
|
197
|
-
|
|
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
|
|
225
|
+
## Decorative versus meaningful images
|
|
201
226
|
|
|
202
227
|
```swift
|
|
203
|
-
|
|
204
|
-
Image(
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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("
|
|
234
|
+
.accessibilityLabel("Filters") // icon-only button needs a name
|
|
215
235
|
```
|
|
216
236
|
|
|
217
|
-
|
|
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
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
-
|
|
226
|
-
|
|
227
|
-
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
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
|
-
|
|
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
|
-
|
|
267
|
-
@Environment(\.accessibilityAssistiveAccessEnabled) var
|
|
318
|
+
struct LibraryScreen: View {
|
|
319
|
+
@Environment(\.accessibilityAssistiveAccessEnabled) private var assistiveAccess
|
|
268
320
|
|
|
269
|
-
var body: some View {
|
|
270
|
-
|
|
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
|
-
|
|
279
|
-
-
|
|
280
|
-
-
|
|
281
|
-
-
|
|
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
|
-
|
|
332
|
+
From iOS 26 an app can also declare `UISupportsAssistiveAccess` in its
|
|
333
|
+
Info.plist and provide a dedicated `AssistiveAccess` scene.
|
|
285
334
|
|
|
286
|
-
|
|
335
|
+
## UIKit
|
|
287
336
|
|
|
288
|
-
-
|
|
289
|
-
-
|
|
290
|
-
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
-
|
|
294
|
-
- Post `.
|
|
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
|
-
|
|
297
|
-
// UIKit trait modification
|
|
298
|
-
customButton.accessibilityTraits.insert(.button)
|
|
299
|
-
customButton.accessibilityTraits.remove(.staticText)
|
|
346
|
+
## AppKit
|
|
300
347
|
|
|
301
|
-
|
|
302
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
315
|
-
|
|
316
|
-
|
|
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
|
-
|
|
342
|
-
.accessibilityCustomContent("
|
|
343
|
-
.accessibilityCustomContent("
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
373
|
-
func testProductRowAccessibility() throws {
|
|
374
|
-
let app = XCUIApplication()
|
|
375
|
-
app.launch()
|
|
392
|
+
## Testing
|
|
376
393
|
|
|
377
|
-
|
|
378
|
-
XCTAssertTrue(productCell.exists)
|
|
379
|
-
XCTAssertTrue(productCell.isEnabled)
|
|
394
|
+
### By hand
|
|
380
395
|
|
|
381
|
-
|
|
382
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
412
|
+
@MainActor
|
|
413
|
+
func testFocusReturnsAfterSheet() {
|
|
397
414
|
let app = XCUIApplication()
|
|
398
415
|
app.launch()
|
|
399
416
|
|
|
400
|
-
let
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
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
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
-
|
|
430
|
-
-
|
|
431
|
-
-
|
|
432
|
-
|
|
433
|
-
-
|
|
434
|
-
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
- [ ]
|
|
440
|
-
- [ ]
|
|
441
|
-
- [ ]
|
|
442
|
-
|
|
443
|
-
- [ ]
|
|
444
|
-
|
|
445
|
-
- [ ]
|
|
446
|
-
- [ ]
|
|
447
|
-
|
|
448
|
-
- [ ]
|
|
449
|
-
- [ ]
|
|
450
|
-
- [ ]
|
|
451
|
-
- [ ]
|
|
452
|
-
- [ ]
|
|
453
|
-
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
- [
|
|
458
|
-
|
|
459
|
-
- [
|
|
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.
|