buildanything 1.7.0 → 1.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +55 -0
- package/README.md +71 -61
- package/agents/ios-app-review-guardian.md +49 -0
- package/agents/ios-foundation-models-specialist.md +46 -0
- package/agents/ios-storekit-specialist.md +52 -0
- package/agents/ios-swift-architect.md +102 -0
- package/agents/ios-swift-search.md +130 -0
- package/agents/ios-swift-ui-design.md +104 -0
- package/commands/build.md +80 -176
- package/commands/fix.md +65 -0
- package/commands/setup.md +73 -0
- package/commands/ux-review.md +63 -0
- package/commands/verify.md +72 -0
- package/hooks/session-start +18 -1
- package/package.json +5 -2
- package/protocols/brainstorm.md +99 -0
- package/protocols/build-fix.md +52 -0
- package/protocols/cleanup.md +54 -0
- package/protocols/design.md +269 -0
- package/protocols/eval-harness.md +61 -0
- package/protocols/fake-data-detector.md +64 -0
- package/protocols/ios-context.md +235 -0
- package/protocols/ios-frameworks-map.md +323 -0
- package/protocols/ios-phase-branches.md +162 -0
- package/protocols/ios-preflight.md +27 -0
- package/protocols/metric-loop.md +93 -0
- package/protocols/planning.md +87 -0
- package/protocols/smoke-test.md +110 -0
- package/protocols/verify.md +67 -0
- package/protocols/web-phase-branches.md +201 -0
- package/skills/ios/_VENDORED.md +60 -0
- package/skills/ios/activitykit/LICENSE +131 -0
- package/skills/ios/activitykit/SKILL.md +505 -0
- package/skills/ios/activitykit/references/activitykit-patterns.md +868 -0
- package/skills/ios/app-intents/LICENSE +131 -0
- package/skills/ios/app-intents/SKILL.md +494 -0
- package/skills/ios/app-intents/references/appintents-advanced.md +1076 -0
- package/skills/ios/apple-on-device-ai/LICENSE +131 -0
- package/skills/ios/apple-on-device-ai/SKILL.md +505 -0
- package/skills/ios/apple-on-device-ai/references/coreml-conversion.md +425 -0
- package/skills/ios/apple-on-device-ai/references/coreml-optimization.md +344 -0
- package/skills/ios/apple-on-device-ai/references/foundation-models.md +508 -0
- package/skills/ios/apple-on-device-ai/references/mlx-swift.md +285 -0
- package/skills/ios/ios-26-platform/SKILL.md +53 -0
- package/skills/ios/ios-26-platform/references/automatic-adoption.md +161 -0
- package/skills/ios/ios-26-platform/references/backward-compat.md +238 -0
- package/skills/ios/ios-26-platform/references/liquid-glass.md +255 -0
- package/skills/ios/ios-26-platform/references/swiftui-apis.md +277 -0
- package/skills/ios/ios-26-platform/references/toolbar-navigation.md +250 -0
- package/skills/ios/ios-bootstrap/SKILL.md +98 -0
- package/skills/ios/ios-bootstrap/references/apple-docs-mcp-config.md +28 -0
- package/skills/ios/ios-bootstrap/references/new-project-dialog.md +41 -0
- package/skills/ios/ios-bootstrap/references/xcode-mcp-config.md +29 -0
- package/skills/ios/ios-debugger-agent/LICENSE +21 -0
- package/skills/ios/ios-debugger-agent/SKILL.md +58 -0
- package/skills/ios/ios-debugger-agent/agents/openai.yaml +4 -0
- package/skills/ios/ios-entitlements-generator/SKILL.md +47 -0
- package/skills/ios/ios-hig/SKILL.md +41 -0
- package/skills/ios/ios-hig/references/accessibility.md +81 -0
- package/skills/ios/ios-hig/references/content.md +142 -0
- package/skills/ios/ios-hig/references/feedback.md +123 -0
- package/skills/ios/ios-hig/references/interaction.md +199 -0
- package/skills/ios/ios-hig/references/performance-platform.md +129 -0
- package/skills/ios/ios-hig/references/privacy-permissions.md +181 -0
- package/skills/ios/ios-hig/references/visual-design.md +84 -0
- package/skills/ios/ios-info-plist-hardening/SKILL.md +130 -0
- package/skills/ios/ios-maestro-flow-author/SKILL.md +68 -0
- package/skills/ios/ios-maestro-flow-author/references/input-and-scroll.yaml +17 -0
- package/skills/ios/ios-maestro-flow-author/references/modal-and-dismiss.yaml +14 -0
- package/skills/ios/ios-maestro-flow-author/references/onboarding-flow.yaml +16 -0
- package/skills/ios/ios-maestro-flow-author/references/tab-navigation.yaml +13 -0
- package/skills/ios/ios-maestro-flow-author/references/tap-and-assert.yaml +9 -0
- package/skills/ios/swift-accessibility/LICENSE +21 -0
- package/skills/ios/swift-accessibility/SKILL.md +371 -0
- package/skills/ios/swift-accessibility/examples/before-after-appkit.md +446 -0
- package/skills/ios/swift-accessibility/examples/before-after-swiftui.md +441 -0
- package/skills/ios/swift-accessibility/examples/before-after-uikit.md +464 -0
- package/skills/ios/swift-accessibility/references/assistive-access.md +441 -0
- package/skills/ios/swift-accessibility/references/display-settings.md +491 -0
- package/skills/ios/swift-accessibility/references/dynamic-type.md +420 -0
- package/skills/ios/swift-accessibility/references/media-accessibility.md +421 -0
- package/skills/ios/swift-accessibility/references/motor-input.md +393 -0
- package/skills/ios/swift-accessibility/references/nutrition-labels.md +362 -0
- package/skills/ios/swift-accessibility/references/platform-specifics.md +515 -0
- package/skills/ios/swift-accessibility/references/semantic-structure.md +585 -0
- package/skills/ios/swift-accessibility/references/testing-auditing.md +507 -0
- package/skills/ios/swift-accessibility/references/voice-control.md +317 -0
- package/skills/ios/swift-accessibility/references/voiceover-swiftui.md +584 -0
- package/skills/ios/swift-accessibility/references/voiceover-uikit.md +519 -0
- package/skills/ios/swift-accessibility/references/wcag-mapping.md +167 -0
- package/skills/ios/swift-accessibility/resources/audit-template.swift +128 -0
- package/skills/ios/swift-accessibility/resources/qa-checklist.md +258 -0
- package/skills/ios/swift-concurrency/LICENSE +21 -0
- package/skills/ios/swift-concurrency/SKILL.md +171 -0
- package/skills/ios/swift-concurrency/references/_index.md +50 -0
- package/skills/ios/swift-concurrency/references/actors.md +660 -0
- package/skills/ios/swift-concurrency/references/async-algorithms.md +847 -0
- package/skills/ios/swift-concurrency/references/async-await-basics.md +266 -0
- package/skills/ios/swift-concurrency/references/async-sequences.md +710 -0
- package/skills/ios/swift-concurrency/references/core-data.md +560 -0
- package/skills/ios/swift-concurrency/references/glossary.md +135 -0
- package/skills/ios/swift-concurrency/references/linting.md +155 -0
- package/skills/ios/swift-concurrency/references/memory-management.md +569 -0
- package/skills/ios/swift-concurrency/references/migration.md +1104 -0
- package/skills/ios/swift-concurrency/references/performance.md +593 -0
- package/skills/ios/swift-concurrency/references/sendable.md +598 -0
- package/skills/ios/swift-concurrency/references/tasks.md +636 -0
- package/skills/ios/swift-concurrency/references/testing.md +592 -0
- package/skills/ios/swift-concurrency/references/threading.md +495 -0
- package/skills/ios/swift-security-expert/LICENSE +21 -0
- package/skills/ios/swift-security-expert/SKILL.md +470 -0
- package/skills/ios/swift-security-expert/references/biometric-authentication.md +565 -0
- package/skills/ios/swift-security-expert/references/certificate-trust.md +592 -0
- package/skills/ios/swift-security-expert/references/common-anti-patterns.md +690 -0
- package/skills/ios/swift-security-expert/references/compliance-owasp-mapping.md +537 -0
- package/skills/ios/swift-security-expert/references/credential-storage-patterns.md +721 -0
- package/skills/ios/swift-security-expert/references/cryptokit-public-key.md +505 -0
- package/skills/ios/swift-security-expert/references/cryptokit-symmetric.md +497 -0
- package/skills/ios/swift-security-expert/references/keychain-access-control.md +508 -0
- package/skills/ios/swift-security-expert/references/keychain-fundamentals.md +596 -0
- package/skills/ios/swift-security-expert/references/keychain-item-classes.md +476 -0
- package/skills/ios/swift-security-expert/references/keychain-sharing.md +458 -0
- package/skills/ios/swift-security-expert/references/migration-legacy-stores.md +727 -0
- package/skills/ios/swift-security-expert/references/secure-enclave.md +539 -0
- package/skills/ios/swift-security-expert/references/testing-security-code.md +781 -0
- package/skills/ios/swift-testing-expert/LICENSE +21 -0
- package/skills/ios/swift-testing-expert/SKILL.md +79 -0
- package/skills/ios/swift-testing-expert/references/_index.md +12 -0
- package/skills/ios/swift-testing-expert/references/async-testing-and-waiting.md +127 -0
- package/skills/ios/swift-testing-expert/references/expectations.md +145 -0
- package/skills/ios/swift-testing-expert/references/fundamentals.md +141 -0
- package/skills/ios/swift-testing-expert/references/migration-from-xctest.md +127 -0
- package/skills/ios/swift-testing-expert/references/parallelization-and-isolation.md +95 -0
- package/skills/ios/swift-testing-expert/references/parameterized-testing.md +284 -0
- package/skills/ios/swift-testing-expert/references/performance-and-best-practices.md +187 -0
- package/skills/ios/swift-testing-expert/references/traits-and-tags.md +114 -0
- package/skills/ios/swift-testing-expert/references/xcode-workflows.md +70 -0
- package/skills/ios/swiftdata-pro/LICENSE +21 -0
- package/skills/ios/swiftdata-pro/SKILL.md +102 -0
- package/skills/ios/swiftdata-pro/agents/openai.yaml +10 -0
- package/skills/ios/swiftdata-pro/assets/swiftdata-pro-icon.png +0 -0
- package/skills/ios/swiftdata-pro/assets/swiftdata-pro-icon.svg +29 -0
- package/skills/ios/swiftdata-pro/references/class-inheritance.md +104 -0
- package/skills/ios/swiftdata-pro/references/cloudkit.md +10 -0
- package/skills/ios/swiftdata-pro/references/core-rules.md +20 -0
- package/skills/ios/swiftdata-pro/references/indexing.md +27 -0
- package/skills/ios/swiftdata-pro/references/predicates.md +73 -0
- package/skills/ios/swiftui-design-principles/AGENTS.md +21 -0
- package/skills/ios/swiftui-design-principles/LICENSE +21 -0
- package/skills/ios/swiftui-design-principles/README.md +41 -0
- package/skills/ios/swiftui-design-principles/SKILL.md +605 -0
- package/skills/ios/swiftui-design-principles/metadata.json +10 -0
- package/skills/ios/swiftui-liquid-glass/LICENSE +21 -0
- package/skills/ios/swiftui-liquid-glass/SKILL.md +95 -0
- package/skills/ios/swiftui-liquid-glass/agents/openai.yaml +4 -0
- package/skills/ios/swiftui-liquid-glass/references/liquid-glass.md +280 -0
- package/skills/ios/swiftui-performance-audit/LICENSE +21 -0
- package/skills/ios/swiftui-performance-audit/SKILL.md +111 -0
- package/skills/ios/swiftui-performance-audit/agents/openai.yaml +4 -0
- package/skills/ios/swiftui-performance-audit/references/code-smells.md +150 -0
- package/skills/ios/swiftui-performance-audit/references/demystify-swiftui-performance-wwdc23.md +46 -0
- package/skills/ios/swiftui-performance-audit/references/optimizing-swiftui-performance-instruments.md +29 -0
- package/skills/ios/swiftui-performance-audit/references/profiling-intake.md +44 -0
- package/skills/ios/swiftui-performance-audit/references/report-template.md +47 -0
- package/skills/ios/swiftui-performance-audit/references/understanding-hangs-in-your-app.md +33 -0
- package/skills/ios/swiftui-performance-audit/references/understanding-improving-swiftui-performance.md +52 -0
- package/skills/ios/swiftui-pro/LICENSE +21 -0
- package/skills/ios/swiftui-pro/SKILL.md +108 -0
- package/skills/ios/swiftui-pro/agents/openai.yaml +10 -0
- package/skills/ios/swiftui-pro/assets/swiftui-pro-icon.png +0 -0
- package/skills/ios/swiftui-pro/assets/swiftui-pro-icon.svg +29 -0
- package/skills/ios/swiftui-pro/references/accessibility.md +13 -0
- package/skills/ios/swiftui-pro/references/api.md +39 -0
- package/skills/ios/swiftui-pro/references/data.md +43 -0
- package/skills/ios/swiftui-pro/references/design.md +31 -0
- package/skills/ios/swiftui-pro/references/hygiene.md +9 -0
- package/skills/ios/swiftui-pro/references/navigation.md +14 -0
- package/skills/ios/swiftui-pro/references/performance.md +46 -0
- package/skills/ios/swiftui-pro/references/swift.md +56 -0
- package/skills/ios/swiftui-pro/references/views.md +35 -0
- package/skills/ios/swiftui-ui-patterns/LICENSE +21 -0
- package/skills/ios/swiftui-ui-patterns/SKILL.md +100 -0
- package/skills/ios/swiftui-ui-patterns/agents/openai.yaml +4 -0
- package/skills/ios/swiftui-ui-patterns/references/app-wiring.md +201 -0
- package/skills/ios/swiftui-ui-patterns/references/async-state.md +96 -0
- package/skills/ios/swiftui-ui-patterns/references/components-index.md +50 -0
- package/skills/ios/swiftui-ui-patterns/references/controls.md +57 -0
- package/skills/ios/swiftui-ui-patterns/references/deeplinks.md +66 -0
- package/skills/ios/swiftui-ui-patterns/references/focus.md +90 -0
- package/skills/ios/swiftui-ui-patterns/references/form.md +97 -0
- package/skills/ios/swiftui-ui-patterns/references/grids.md +71 -0
- package/skills/ios/swiftui-ui-patterns/references/haptics.md +71 -0
- package/skills/ios/swiftui-ui-patterns/references/input-toolbar.md +51 -0
- package/skills/ios/swiftui-ui-patterns/references/lightweight-clients.md +93 -0
- package/skills/ios/swiftui-ui-patterns/references/list.md +86 -0
- package/skills/ios/swiftui-ui-patterns/references/loading-placeholders.md +38 -0
- package/skills/ios/swiftui-ui-patterns/references/macos-settings.md +71 -0
- package/skills/ios/swiftui-ui-patterns/references/matched-transitions.md +59 -0
- package/skills/ios/swiftui-ui-patterns/references/media.md +73 -0
- package/skills/ios/swiftui-ui-patterns/references/menu-bar.md +101 -0
- package/skills/ios/swiftui-ui-patterns/references/navigationstack.md +159 -0
- package/skills/ios/swiftui-ui-patterns/references/overlay.md +45 -0
- package/skills/ios/swiftui-ui-patterns/references/performance.md +62 -0
- package/skills/ios/swiftui-ui-patterns/references/previews.md +48 -0
- package/skills/ios/swiftui-ui-patterns/references/scroll-reveal.md +133 -0
- package/skills/ios/swiftui-ui-patterns/references/scrollview.md +87 -0
- package/skills/ios/swiftui-ui-patterns/references/searchable.md +71 -0
- package/skills/ios/swiftui-ui-patterns/references/sheets.md +155 -0
- package/skills/ios/swiftui-ui-patterns/references/split-views.md +72 -0
- package/skills/ios/swiftui-ui-patterns/references/tabview.md +114 -0
- package/skills/ios/swiftui-ui-patterns/references/theming.md +71 -0
- package/skills/ios/swiftui-ui-patterns/references/title-menus.md +93 -0
- package/skills/ios/swiftui-ui-patterns/references/top-bar.md +49 -0
- package/skills/ios/swiftui-view-refactor/LICENSE +21 -0
- package/skills/ios/swiftui-view-refactor/SKILL.md +207 -0
- package/skills/ios/swiftui-view-refactor/agents/openai.yaml +4 -0
- package/skills/ios/swiftui-view-refactor/references/mv-patterns.md +161 -0
- package/skills/ios/widgetkit/LICENSE +131 -0
- package/skills/ios/widgetkit/SKILL.md +502 -0
- package/skills/ios/widgetkit/references/widgetkit-advanced.md +871 -0
|
@@ -0,0 +1,507 @@
|
|
|
1
|
+
# Testing and Auditing
|
|
2
|
+
|
|
3
|
+
Covers Accessibility Inspector, SwiftUI Previews, XCTest / XCUITest, manual testing procedures, and common audit findings — for verifying Accessibility Nutrition Label readiness.
|
|
4
|
+
|
|
5
|
+
## Contents
|
|
6
|
+
- [Accessibility Inspector (Xcode)](#accessibility-inspector-xcode)
|
|
7
|
+
- [Verifying Accessibility in Xcode](#verifying-accessibility-in-xcode)
|
|
8
|
+
- [XCUITest — Automated Accessibility Testing](#xcuitest--automated-accessibility-testing)
|
|
9
|
+
- [Manual Testing Checklist](#manual-testing-checklist)
|
|
10
|
+
- [Common Audit Findings](#common-audit-findings)
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Accessibility Inspector (Xcode)
|
|
15
|
+
|
|
16
|
+
The primary tool for inspecting and auditing accessibility without a real device. Available via Xcode → Open Developer Tool → Accessibility Inspector.
|
|
17
|
+
|
|
18
|
+
### Inspection Mode
|
|
19
|
+
|
|
20
|
+
Point at any element in the Simulator or on a connected Mac app. Inspector shows:
|
|
21
|
+
- `accessibilityLabel`, `accessibilityHint`, `accessibilityValue`
|
|
22
|
+
- `accessibilityTraits` (button, header, selected, etc.)
|
|
23
|
+
- `accessibilityFrame` (tap target size in points)
|
|
24
|
+
- Container information
|
|
25
|
+
|
|
26
|
+
**Usage:** Click the crosshair icon, then hover over elements in the Simulator. Verify that every element reports the correct label and traits. Confirm tap targets are ≥ 44×44pt.
|
|
27
|
+
|
|
28
|
+
### Audit Tab
|
|
29
|
+
|
|
30
|
+
Runs automated checks across the current screen.
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
Accessibility Inspector → Audit → Run Audit
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Findings include:
|
|
37
|
+
- Missing labels on interactive elements
|
|
38
|
+
- Low color contrast (compares against WCAG thresholds)
|
|
39
|
+
- Touch targets below 44×44pt
|
|
40
|
+
- Elements with traits that contradict their role
|
|
41
|
+
- Decorative images exposed to accessibility tree
|
|
42
|
+
|
|
43
|
+
**Workflow:** Fix every finding before proceeding to manual testing. Treat high-severity findings as blockers.
|
|
44
|
+
|
|
45
|
+
### Contrast Checker
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
Accessibility Inspector → Inspection → Color tab → Use eyedropper
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Measures contrast ratio between two colors. Validates against:
|
|
52
|
+
- 4.5:1 for normal text
|
|
53
|
+
- 3:1 for large text and non-text interactive elements
|
|
54
|
+
|
|
55
|
+
Test every color pair: foreground/background for body text, button labels, placeholder text, and state indicators.
|
|
56
|
+
|
|
57
|
+
### Settings Tab (Simulate Accessibility Settings)
|
|
58
|
+
|
|
59
|
+
Simulate device settings without changing actual device configuration:
|
|
60
|
+
- Increase Contrast
|
|
61
|
+
- Reduce Motion
|
|
62
|
+
- Bold Text
|
|
63
|
+
- Button Shapes
|
|
64
|
+
- Reduce Transparency
|
|
65
|
+
- Grayscale (via Simulator)
|
|
66
|
+
- Dynamic Type sizes
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Verifying Accessibility in Xcode
|
|
71
|
+
|
|
72
|
+
Use Xcode's built-in Canvas tools and Accessibility Inspector to test accessibility configurations without writing custom preview code. Many accessibility environment values (`colorSchemeContrast`, `accessibilityReduceMotion`, `accessibilityReduceTransparency`, `accessibilityDifferentiateWithoutColor`) are **read-only** — `.environment()` calls for these values compile but are **silently ignored** at runtime. Use Accessibility Inspector (Settings tab) or Simulator settings instead.
|
|
73
|
+
|
|
74
|
+
### Xcode Canvas Variants
|
|
75
|
+
|
|
76
|
+
The preview canvas has a **Variants** button (grid icon) at the bottom. Click it to choose:
|
|
77
|
+
|
|
78
|
+
| Variant mode | What it shows |
|
|
79
|
+
|---|---|
|
|
80
|
+
| **Dynamic Type Variants** | Your view rendered at all 12 Dynamic Type sizes side by side — catches clipping, overlap, and truncation |
|
|
81
|
+
| **Color Scheme Variants** | Light and dark mode previews — catches contrast failures and invisible borders |
|
|
82
|
+
| **Orientation Variants** | Portrait + landscape — catches layout breaks |
|
|
83
|
+
|
|
84
|
+
This is the fastest way to visually verify Dynamic Type and Dark Mode without writing any code.
|
|
85
|
+
|
|
86
|
+
### Xcode Canvas Device Settings
|
|
87
|
+
|
|
88
|
+
The preview canvas has a **Device Settings** button (slider icon) at the bottom. Use it to configure a single preview with:
|
|
89
|
+
|
|
90
|
+
- **Color Scheme**: light / dark
|
|
91
|
+
- **Dynamic Type size**: any of the 12 sizes
|
|
92
|
+
- **Orientation**: portrait / landscape
|
|
93
|
+
|
|
94
|
+
Combine these to test specific scenarios (e.g., dark mode + accessibility large text + landscape).
|
|
95
|
+
|
|
96
|
+
### Accessibility Inspector — Settings Tab
|
|
97
|
+
|
|
98
|
+
For read-only accessibility settings, use Accessibility Inspector on the Simulator:
|
|
99
|
+
|
|
100
|
+
**Open:** Xcode menu → Open Developer Tool → Accessibility Inspector
|
|
101
|
+
|
|
102
|
+
**Settings tab** — toggle these on the Simulator without changing device settings:
|
|
103
|
+
|
|
104
|
+
| Setting | What it simulates |
|
|
105
|
+
|---|---|
|
|
106
|
+
| Increase Contrast | Tests `colorSchemeContrast == .increased` |
|
|
107
|
+
| Reduce Motion | Tests `accessibilityReduceMotion == true` |
|
|
108
|
+
| Bold Text | Tests `legibilityWeight == .bold` |
|
|
109
|
+
| Reduce Transparency | Tests `accessibilityReduceTransparency == true` |
|
|
110
|
+
| Button Shapes | Tests `accessibilityShowButtonShapes == true` |
|
|
111
|
+
| Grayscale | Tests color-only indicators (via Simulator Color Filters) |
|
|
112
|
+
| Dynamic Type | Adjusts text size on the Simulator |
|
|
113
|
+
|
|
114
|
+
**Workflow:** Enable each setting, then interact with the Simulator to verify your UI adapts correctly.
|
|
115
|
+
|
|
116
|
+
### Writable environment values for `#Preview`
|
|
117
|
+
|
|
118
|
+
Only these accessibility-related values are writable and work in `#Preview` with `.environment()`:
|
|
119
|
+
|
|
120
|
+
```swift
|
|
121
|
+
#Preview("Large Text") {
|
|
122
|
+
ProductCardView(product: .sample)
|
|
123
|
+
.environment(\.dynamicTypeSize, .accessibility3)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
#Preview("Dark Mode") {
|
|
127
|
+
ProductCardView(product: .sample)
|
|
128
|
+
.environment(\.colorScheme, .dark)
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
For testing adaptive layout breakpoints:
|
|
133
|
+
|
|
134
|
+
```swift
|
|
135
|
+
#Preview("Before breakpoint") {
|
|
136
|
+
AdaptiveCardView()
|
|
137
|
+
.environment(\.dynamicTypeSize, .xxxLarge)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
#Preview("After breakpoint") {
|
|
141
|
+
AdaptiveCardView()
|
|
142
|
+
.environment(\.dynamicTypeSize, .accessibility1)
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### What to check for each setting
|
|
147
|
+
|
|
148
|
+
| Setting | Check for |
|
|
149
|
+
|---|---|
|
|
150
|
+
| Dynamic Type (large sizes) | Text clipping, overlapping elements, truncated labels without `...` affordance |
|
|
151
|
+
| Dark Mode | Unreadable text, invisible borders, low-contrast icons |
|
|
152
|
+
| Increase Contrast | Borders/separators visible, text contrast improved |
|
|
153
|
+
| Reduce Motion | No animations playing, state changes still visible via opacity/fade |
|
|
154
|
+
| Grayscale | Status indicators still distinguishable by shape/icon/text |
|
|
155
|
+
| Bold Text | Layout doesn't break with heavier font weights |
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## XCUITest — Automated Accessibility Testing
|
|
160
|
+
|
|
161
|
+
### Finding Elements by Accessibility Identifier
|
|
162
|
+
|
|
163
|
+
```swift
|
|
164
|
+
// Set stable identifiers in production code
|
|
165
|
+
TextField("Search", text: $query)
|
|
166
|
+
.accessibilityIdentifier("searchField")
|
|
167
|
+
|
|
168
|
+
Button("Submit") { submit() }
|
|
169
|
+
.accessibilityIdentifier("submitButton")
|
|
170
|
+
|
|
171
|
+
// Find in UI tests
|
|
172
|
+
func testSearchFlow() throws {
|
|
173
|
+
let app = XCUIApplication()
|
|
174
|
+
app.launch()
|
|
175
|
+
|
|
176
|
+
let searchField = app.textFields["searchField"]
|
|
177
|
+
XCTAssert(searchField.exists, "Search field must exist")
|
|
178
|
+
XCTAssert(searchField.isEnabled, "Search field must be enabled")
|
|
179
|
+
|
|
180
|
+
searchField.tap()
|
|
181
|
+
searchField.typeText("accessibility")
|
|
182
|
+
|
|
183
|
+
let submitButton = app.buttons["submitButton"]
|
|
184
|
+
XCTAssert(submitButton.exists)
|
|
185
|
+
submitButton.tap()
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Querying by Accessibility Label
|
|
190
|
+
|
|
191
|
+
```swift
|
|
192
|
+
// Find by label (matches accessibilityLabel)
|
|
193
|
+
let shareButton = app.buttons["Share"]
|
|
194
|
+
XCTAssert(shareButton.exists)
|
|
195
|
+
|
|
196
|
+
// Find by partial label
|
|
197
|
+
let deleteButtons = app.buttons.matching(identifier: "Delete")
|
|
198
|
+
XCTAssert(deleteButtons.count > 0)
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Printing the Accessibility Tree
|
|
202
|
+
|
|
203
|
+
```swift
|
|
204
|
+
// Invaluable for debugging — prints full accessible element tree
|
|
205
|
+
func testPrintTree() {
|
|
206
|
+
let app = XCUIApplication()
|
|
207
|
+
app.launch()
|
|
208
|
+
print(app.debugDescription)
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Verifying Accessibility Properties
|
|
213
|
+
|
|
214
|
+
```swift
|
|
215
|
+
func testProductCardAccessibility() throws {
|
|
216
|
+
let app = XCUIApplication()
|
|
217
|
+
app.launch()
|
|
218
|
+
|
|
219
|
+
// Verify the card is a single accessible element
|
|
220
|
+
let card = app.otherElements["Product Card"]
|
|
221
|
+
XCTAssert(card.exists)
|
|
222
|
+
|
|
223
|
+
// Verify label is meaningful
|
|
224
|
+
XCTAssertFalse(card.label.isEmpty, "Card must have an accessibility label")
|
|
225
|
+
|
|
226
|
+
// Verify interactive elements have labels
|
|
227
|
+
let favoriteButton = app.buttons["Add to favorites"]
|
|
228
|
+
XCTAssert(favoriteButton.exists, "Favorite button must have correct label")
|
|
229
|
+
|
|
230
|
+
// Verify button is large enough (indirectly via existence and interaction)
|
|
231
|
+
XCTAssert(favoriteButton.isHittable, "Favorite button must be tappable")
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Testing VoiceOver Navigation Flow
|
|
236
|
+
|
|
237
|
+
```swift
|
|
238
|
+
func testVoiceOverReadingOrder() {
|
|
239
|
+
let app = XCUIApplication()
|
|
240
|
+
app.launchArguments = ["-UIAccessibilityEnabled", "YES"]
|
|
241
|
+
app.launch()
|
|
242
|
+
|
|
243
|
+
// Swipe through elements and verify order
|
|
244
|
+
// Note: full VoiceOver simulation is limited in XCUITest
|
|
245
|
+
// Use Accessibility Inspector + manual testing for complete VoiceOver verification
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### performAccessibilityAudit() (iOS 17+ / macOS 14+ / tvOS 17+ / watchOS 10+ / visionOS 1+)
|
|
250
|
+
|
|
251
|
+
Runs Accessibility Inspector's audit engine programmatically inside XCUITest. Catches accessibility regressions automatically in CI — no manual inspection needed. Part of the `XCUIAutomation` framework.
|
|
252
|
+
|
|
253
|
+
#### API Signature
|
|
254
|
+
|
|
255
|
+
```swift
|
|
256
|
+
@MainActor func performAccessibilityAudit(
|
|
257
|
+
for auditTypes: XCUIAccessibilityAuditType = .all,
|
|
258
|
+
_ issueHandler: ((XCUIAccessibilityAuditIssue) throws -> Bool)? = nil
|
|
259
|
+
) throws
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
#### Audit Types
|
|
263
|
+
|
|
264
|
+
| Type | What it checks | Platforms |
|
|
265
|
+
|---|---|---|
|
|
266
|
+
| `.contrast` | WCAG contrast ratio (4.5:1 text, 3:1 non-text) | All |
|
|
267
|
+
| `.elementDetection` | Elements missing from the accessibility tree | All |
|
|
268
|
+
| `.hitRegion` | Touch targets smaller than 44×44pt | All |
|
|
269
|
+
| `.sufficientElementDescription` | Missing or empty accessibility labels | All |
|
|
270
|
+
| `.dynamicType` | Text that doesn't scale with Dynamic Type | All |
|
|
271
|
+
| `.textClipped` | Text clipped or truncated at larger sizes | All |
|
|
272
|
+
| `.trait` | Incorrect or missing accessibility traits | All |
|
|
273
|
+
| `.action` | Missing or invalid accessibility actions | macOS only |
|
|
274
|
+
| `.parentChild` | Parent-child relationship issues in the accessibility tree | macOS only |
|
|
275
|
+
|
|
276
|
+
#### XCUIAccessibilityAuditIssue Properties
|
|
277
|
+
|
|
278
|
+
| Property | Type | Description |
|
|
279
|
+
|---|---|---|
|
|
280
|
+
| `auditType` | `XCUIAccessibilityAuditType` | The audit type that flagged this issue |
|
|
281
|
+
| `element` | `XCUIElement?` | The element with the issue (nil if not identifiable) |
|
|
282
|
+
| `compactDescription` | `String` | Short description of the issue |
|
|
283
|
+
| `detailedDescription` | `String` | Full description with remediation guidance |
|
|
284
|
+
|
|
285
|
+
#### Basic Usage — Run All Checks
|
|
286
|
+
|
|
287
|
+
```swift
|
|
288
|
+
func testAccessibilityAudit() throws {
|
|
289
|
+
let app = XCUIApplication()
|
|
290
|
+
app.launch()
|
|
291
|
+
|
|
292
|
+
// Runs all audit types — fails test on any issue
|
|
293
|
+
try app.performAccessibilityAudit()
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
#### Filter by Audit Type
|
|
298
|
+
|
|
299
|
+
```swift
|
|
300
|
+
func testContrastAndLabels() throws {
|
|
301
|
+
let app = XCUIApplication()
|
|
302
|
+
app.launch()
|
|
303
|
+
|
|
304
|
+
// Run only contrast and label checks
|
|
305
|
+
try app.performAccessibilityAudit(for: [.contrast, .sufficientElementDescription])
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
#### Ignore Known Issues
|
|
310
|
+
|
|
311
|
+
Use the issue handler closure to suppress known issues. Return `true` to ignore an issue, `false` to fail on it. Use `compactDescription` or `detailedDescription` for debugging.
|
|
312
|
+
|
|
313
|
+
```swift
|
|
314
|
+
func testAccessibilityAuditWithExclusions() throws {
|
|
315
|
+
let app = XCUIApplication()
|
|
316
|
+
app.launch()
|
|
317
|
+
|
|
318
|
+
try app.performAccessibilityAudit(for: .all) { issue in
|
|
319
|
+
// Log issue details for debugging
|
|
320
|
+
print(issue.detailedDescription)
|
|
321
|
+
|
|
322
|
+
// Ignore contrast issues on the splash screen (uses branded colors)
|
|
323
|
+
if issue.auditType == .contrast,
|
|
324
|
+
issue.element?.identifier == "splashLogo" {
|
|
325
|
+
return true
|
|
326
|
+
}
|
|
327
|
+
return false
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
#### Exclude Specific Audit Types
|
|
333
|
+
|
|
334
|
+
```swift
|
|
335
|
+
func testAccessibilityAuditExcludingContrast() throws {
|
|
336
|
+
let app = XCUIApplication()
|
|
337
|
+
app.launch()
|
|
338
|
+
|
|
339
|
+
var auditTypes: XCUIAccessibilityAuditType = .all
|
|
340
|
+
auditTypes.remove(.contrast) // Skip contrast checks
|
|
341
|
+
|
|
342
|
+
try app.performAccessibilityAudit(for: auditTypes)
|
|
343
|
+
}
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
#### Multi-Screen Regression Test
|
|
347
|
+
|
|
348
|
+
Use a UI test like this to audit several important screens in one run.
|
|
349
|
+
The test launches the app, navigates through key flows, and runs `performAccessibilityAudit()` after each navigation step.
|
|
350
|
+
This helps catch common regressions such as missing labels, low contrast, clipped text, small hit regions, and incorrect traits.
|
|
351
|
+
|
|
352
|
+
```swift
|
|
353
|
+
class AccessibilityRegressionTests: XCTestCase {
|
|
354
|
+
func testFullAccessibilityAudit() throws {
|
|
355
|
+
let app = XCUIApplication()
|
|
356
|
+
app.launch()
|
|
357
|
+
|
|
358
|
+
// Audit the launch screen.
|
|
359
|
+
try app.performAccessibilityAudit()
|
|
360
|
+
|
|
361
|
+
// Navigate to Settings and audit that screen.
|
|
362
|
+
app.tabBars.buttons["Settings"].tap()
|
|
363
|
+
try app.performAccessibilityAudit()
|
|
364
|
+
|
|
365
|
+
// Navigate to Profile and audit that screen.
|
|
366
|
+
app.tabBars.buttons["Profile"].tap()
|
|
367
|
+
try app.performAccessibilityAudit()
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
This does not replace manual testing with VoiceOver, Voice Control, Switch Control, or real-device checks.
|
|
373
|
+
It only audits the screens the test actually visits, so extend the navigation flow to cover the important user paths in your app.
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
## Manual Testing Checklist
|
|
378
|
+
|
|
379
|
+
### VoiceOver Testing (Requires Real Device)
|
|
380
|
+
|
|
381
|
+
Enable: Settings → Accessibility → VoiceOver
|
|
382
|
+
|
|
383
|
+
| Test | Pass Criteria |
|
|
384
|
+
|---|---|
|
|
385
|
+
| Swipe right through all elements | Every interactive element is reachable |
|
|
386
|
+
| Tap any element | Label, value, and traits are announced |
|
|
387
|
+
| Double-tap interactive element | Correct action performed |
|
|
388
|
+
| Swipe up/down on adjustable element | Value changes (sliders, steppers) |
|
|
389
|
+
| Two-finger Z gesture on modal | Modal dismisses |
|
|
390
|
+
| Rotor navigation | Headings, links, actions all navigable |
|
|
391
|
+
| "Read All" (two-finger swipe up) | Reads entire screen in logical order |
|
|
392
|
+
| Focus after navigation push | Focus moves to first element in new screen |
|
|
393
|
+
| Focus after modal dismiss | Focus returns to trigger element |
|
|
394
|
+
|
|
395
|
+
**Critical flows to test:**
|
|
396
|
+
1. Complete main user task start-to-finish with VoiceOver
|
|
397
|
+
2. Login / account creation
|
|
398
|
+
3. Key purchase or data-entry flow
|
|
399
|
+
4. Settings changes
|
|
400
|
+
|
|
401
|
+
### Voice Control Testing (Requires Real Device)
|
|
402
|
+
|
|
403
|
+
Enable: Settings → Accessibility → Voice Control
|
|
404
|
+
|
|
405
|
+
| Test | Pass Criteria |
|
|
406
|
+
|---|---|
|
|
407
|
+
| "Show numbers" | Every interactive element has a number |
|
|
408
|
+
| "Tap [number]" | Activates correct element |
|
|
409
|
+
| "Show names" | Every element shows a visible text label |
|
|
410
|
+
| "Tap [label]" | Activates element by voice |
|
|
411
|
+
| "Type [text]" | Inserts text in active field |
|
|
412
|
+
| "Select [word]" | Selects matching text |
|
|
413
|
+
| "Delete that" | Deletes selected text |
|
|
414
|
+
| "Scroll down/up" | Scrolls the content |
|
|
415
|
+
|
|
416
|
+
### Switch Control Testing (Requires Real Device or Setting)
|
|
417
|
+
|
|
418
|
+
Enable: Settings → Accessibility → Switch Control
|
|
419
|
+
|
|
420
|
+
| Test | Pass Criteria |
|
|
421
|
+
|---|---|
|
|
422
|
+
| Item scanning | Every element is highlighted in turn |
|
|
423
|
+
| Group scanning | Related groups highlighted as units |
|
|
424
|
+
| Select highlighted element | Correct action triggered |
|
|
425
|
+
| Custom actions available | Actions appear in scanning menu |
|
|
426
|
+
| No timed interactions | No UI times out or auto-advances |
|
|
427
|
+
|
|
428
|
+
### Full Keyboard Access (iPad/Mac)
|
|
429
|
+
|
|
430
|
+
Enable: Settings → Accessibility → Keyboards → Full Keyboard Access
|
|
431
|
+
|
|
432
|
+
| Test | Pass Criteria |
|
|
433
|
+
|---|---|
|
|
434
|
+
| Tab key | Focus moves forward through all interactive elements |
|
|
435
|
+
| Shift+Tab | Focus moves backward |
|
|
436
|
+
| Space/Return | Activates focused element |
|
|
437
|
+
| Escape | Dismisses modal / cancels |
|
|
438
|
+
| Arrow keys | Navigates within pickers, sliders |
|
|
439
|
+
| No focus gaps | Focus never gets stuck in a dead zone |
|
|
440
|
+
|
|
441
|
+
### Dynamic Type
|
|
442
|
+
|
|
443
|
+
Enable: Settings → Accessibility → Display & Text Size → Larger Text → Enable Larger Accessibility Sizes
|
|
444
|
+
|
|
445
|
+
| Size | Test |
|
|
446
|
+
|---|---|
|
|
447
|
+
| Small | Text readable, no clipping |
|
|
448
|
+
| Large (default) | Normal experience |
|
|
449
|
+
| Accessibility Large | Layout adapts, no overlap |
|
|
450
|
+
| Accessibility 5 (max) | All content accessible, nothing truncated without affordance |
|
|
451
|
+
|
|
452
|
+
### Grayscale (Differentiate Without Color)
|
|
453
|
+
|
|
454
|
+
Enable: Settings → Accessibility → Display & Text Size → Color Filters → Grayscale
|
|
455
|
+
|
|
456
|
+
| Test | Pass Criteria |
|
|
457
|
+
|---|---|
|
|
458
|
+
| Status indicators | Still meaningful in grayscale |
|
|
459
|
+
| Charts and graphs | Data distinguishable by shape/position |
|
|
460
|
+
| Error states | Clearly distinguishable from success |
|
|
461
|
+
| All UI | No information lost in grayscale |
|
|
462
|
+
|
|
463
|
+
### Reduce Motion
|
|
464
|
+
|
|
465
|
+
Enable: Settings → Accessibility → Motion → Reduce Motion
|
|
466
|
+
|
|
467
|
+
| Test | Pass Criteria |
|
|
468
|
+
|---|---|
|
|
469
|
+
| Navigation transitions | No sliding; dissolve/fade instead |
|
|
470
|
+
| Animations on state change | Either removed or replaced with fade |
|
|
471
|
+
| Auto-playing content | Stopped or manual control provided |
|
|
472
|
+
| Loading animations | Replaced or removed |
|
|
473
|
+
|
|
474
|
+
### Dark Mode + Increase Contrast
|
|
475
|
+
|
|
476
|
+
Enable: Settings → Appearance → Dark, AND Settings → Accessibility → Display & Text Size → Increase Contrast
|
|
477
|
+
|
|
478
|
+
| Test | Pass Criteria |
|
|
479
|
+
|---|---|
|
|
480
|
+
| All text | Readable against dark background |
|
|
481
|
+
| Borders and separators | Visible |
|
|
482
|
+
| Status indicators | High contrast |
|
|
483
|
+
| Images on dark background | No white halo effect |
|
|
484
|
+
|
|
485
|
+
---
|
|
486
|
+
|
|
487
|
+
## Common Audit Findings
|
|
488
|
+
|
|
489
|
+
| Finding | Severity | Detection | Fix |
|
|
490
|
+
|---|---|---|---|
|
|
491
|
+
| Missing label on icon button | Blocks Assistive Tech | Accessibility Inspector → Missing label warning | `.accessibilityLabel("Share")` |
|
|
492
|
+
| Decorative image announced | Degrades Experience | VO reads image name or "image" | `.accessibilityHidden(true)` |
|
|
493
|
+
| Touch target < 44pt | Degrades Experience | Inspector → Accessibility Frame < 44×44 | `.frame(minWidth: 44, minHeight: 44)` or `.contentShape` |
|
|
494
|
+
| State embedded in label | Degrades Experience | Inspector → label changes on toggle | Use `.accessibilityAddTraits(.isSelected)` |
|
|
495
|
+
| Wrong reading order | Degrades Experience | VO navigation mismatches visual | `.accessibilitySortPriority` or `accessibilityElements` |
|
|
496
|
+
| Color-only status indicator | Incomplete Support | Grayscale filter test | Add shape/icon/text redundancy |
|
|
497
|
+
| Text contrast fails dark mode | Incomplete Support | Inspector contrast checker | Use semantic colors, test both modes |
|
|
498
|
+
| Animation with Reduce Motion | Incomplete Support | Enable Reduce Motion, check for motion | Gate `withAnimation` or provide `.opacity` transition |
|
|
499
|
+
| No custom rotor for long lists | Incomplete Support | VoiceOver navigation test | Add `accessibilityRotor` |
|
|
500
|
+
| Modal doesn't trap VoiceOver | Blocks Assistive Tech | VO can reach background content | `accessibilityViewIsModal = true` |
|
|
501
|
+
| No focus movement after push | Degrades Experience | VO stays on previous screen | Post `.screenChanged` after navigation |
|
|
502
|
+
| Custom text truncates at any size | Incomplete Support | Dynamic Type max size test | `fixedSize()` → scroll, or adaptive layout |
|
|
503
|
+
| Missing `accessibilityPerformEscape` | Blocks Assistive Tech | VO two-finger Z doesn't dismiss | Implement `accessibilityPerformEscape()` |
|
|
504
|
+
| Voice Control element missing | Blocks Assistive Tech | "Show numbers" test | Use `Button` or add `.accessibilityTraits(.button)` |
|
|
505
|
+
| Hint describes action not result | Degrades Experience | Listen to VO hint announcement | Rewrite: "Saves your changes" not "Tap to save" |
|
|
506
|
+
|
|
507
|
+
> **Automation tip:** `performAccessibilityAudit()` (iOS 17+) automatically detects missing labels, low contrast, small hit regions, clipped text, missing traits, and Dynamic Type failures. Run it in CI to catch most of these findings before manual testing.
|