buildanything 1.7.1 → 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.
Files changed (221) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +55 -0
  4. package/agents/ios-app-review-guardian.md +49 -0
  5. package/agents/ios-foundation-models-specialist.md +46 -0
  6. package/agents/ios-storekit-specialist.md +52 -0
  7. package/agents/ios-swift-architect.md +102 -0
  8. package/agents/ios-swift-search.md +130 -0
  9. package/agents/ios-swift-ui-design.md +104 -0
  10. package/commands/build.md +80 -176
  11. package/commands/fix.md +65 -0
  12. package/commands/setup.md +73 -0
  13. package/commands/ux-review.md +63 -0
  14. package/commands/verify.md +72 -0
  15. package/hooks/session-start +18 -1
  16. package/package.json +5 -2
  17. package/protocols/brainstorm.md +99 -0
  18. package/protocols/build-fix.md +52 -0
  19. package/protocols/cleanup.md +54 -0
  20. package/protocols/design.md +269 -0
  21. package/protocols/eval-harness.md +61 -0
  22. package/protocols/fake-data-detector.md +64 -0
  23. package/protocols/ios-context.md +235 -0
  24. package/protocols/ios-frameworks-map.md +323 -0
  25. package/protocols/ios-phase-branches.md +162 -0
  26. package/protocols/ios-preflight.md +27 -0
  27. package/protocols/metric-loop.md +93 -0
  28. package/protocols/planning.md +87 -0
  29. package/protocols/smoke-test.md +110 -0
  30. package/protocols/verify.md +67 -0
  31. package/protocols/web-phase-branches.md +201 -0
  32. package/skills/ios/_VENDORED.md +60 -0
  33. package/skills/ios/activitykit/LICENSE +131 -0
  34. package/skills/ios/activitykit/SKILL.md +505 -0
  35. package/skills/ios/activitykit/references/activitykit-patterns.md +868 -0
  36. package/skills/ios/app-intents/LICENSE +131 -0
  37. package/skills/ios/app-intents/SKILL.md +494 -0
  38. package/skills/ios/app-intents/references/appintents-advanced.md +1076 -0
  39. package/skills/ios/apple-on-device-ai/LICENSE +131 -0
  40. package/skills/ios/apple-on-device-ai/SKILL.md +505 -0
  41. package/skills/ios/apple-on-device-ai/references/coreml-conversion.md +425 -0
  42. package/skills/ios/apple-on-device-ai/references/coreml-optimization.md +344 -0
  43. package/skills/ios/apple-on-device-ai/references/foundation-models.md +508 -0
  44. package/skills/ios/apple-on-device-ai/references/mlx-swift.md +285 -0
  45. package/skills/ios/ios-26-platform/SKILL.md +53 -0
  46. package/skills/ios/ios-26-platform/references/automatic-adoption.md +161 -0
  47. package/skills/ios/ios-26-platform/references/backward-compat.md +238 -0
  48. package/skills/ios/ios-26-platform/references/liquid-glass.md +255 -0
  49. package/skills/ios/ios-26-platform/references/swiftui-apis.md +277 -0
  50. package/skills/ios/ios-26-platform/references/toolbar-navigation.md +250 -0
  51. package/skills/ios/ios-bootstrap/SKILL.md +98 -0
  52. package/skills/ios/ios-bootstrap/references/apple-docs-mcp-config.md +28 -0
  53. package/skills/ios/ios-bootstrap/references/new-project-dialog.md +41 -0
  54. package/skills/ios/ios-bootstrap/references/xcode-mcp-config.md +29 -0
  55. package/skills/ios/ios-debugger-agent/LICENSE +21 -0
  56. package/skills/ios/ios-debugger-agent/SKILL.md +58 -0
  57. package/skills/ios/ios-debugger-agent/agents/openai.yaml +4 -0
  58. package/skills/ios/ios-entitlements-generator/SKILL.md +47 -0
  59. package/skills/ios/ios-hig/SKILL.md +41 -0
  60. package/skills/ios/ios-hig/references/accessibility.md +81 -0
  61. package/skills/ios/ios-hig/references/content.md +142 -0
  62. package/skills/ios/ios-hig/references/feedback.md +123 -0
  63. package/skills/ios/ios-hig/references/interaction.md +199 -0
  64. package/skills/ios/ios-hig/references/performance-platform.md +129 -0
  65. package/skills/ios/ios-hig/references/privacy-permissions.md +181 -0
  66. package/skills/ios/ios-hig/references/visual-design.md +84 -0
  67. package/skills/ios/ios-info-plist-hardening/SKILL.md +130 -0
  68. package/skills/ios/ios-maestro-flow-author/SKILL.md +68 -0
  69. package/skills/ios/ios-maestro-flow-author/references/input-and-scroll.yaml +17 -0
  70. package/skills/ios/ios-maestro-flow-author/references/modal-and-dismiss.yaml +14 -0
  71. package/skills/ios/ios-maestro-flow-author/references/onboarding-flow.yaml +16 -0
  72. package/skills/ios/ios-maestro-flow-author/references/tab-navigation.yaml +13 -0
  73. package/skills/ios/ios-maestro-flow-author/references/tap-and-assert.yaml +9 -0
  74. package/skills/ios/swift-accessibility/LICENSE +21 -0
  75. package/skills/ios/swift-accessibility/SKILL.md +371 -0
  76. package/skills/ios/swift-accessibility/examples/before-after-appkit.md +446 -0
  77. package/skills/ios/swift-accessibility/examples/before-after-swiftui.md +441 -0
  78. package/skills/ios/swift-accessibility/examples/before-after-uikit.md +464 -0
  79. package/skills/ios/swift-accessibility/references/assistive-access.md +441 -0
  80. package/skills/ios/swift-accessibility/references/display-settings.md +491 -0
  81. package/skills/ios/swift-accessibility/references/dynamic-type.md +420 -0
  82. package/skills/ios/swift-accessibility/references/media-accessibility.md +421 -0
  83. package/skills/ios/swift-accessibility/references/motor-input.md +393 -0
  84. package/skills/ios/swift-accessibility/references/nutrition-labels.md +362 -0
  85. package/skills/ios/swift-accessibility/references/platform-specifics.md +515 -0
  86. package/skills/ios/swift-accessibility/references/semantic-structure.md +585 -0
  87. package/skills/ios/swift-accessibility/references/testing-auditing.md +507 -0
  88. package/skills/ios/swift-accessibility/references/voice-control.md +317 -0
  89. package/skills/ios/swift-accessibility/references/voiceover-swiftui.md +584 -0
  90. package/skills/ios/swift-accessibility/references/voiceover-uikit.md +519 -0
  91. package/skills/ios/swift-accessibility/references/wcag-mapping.md +167 -0
  92. package/skills/ios/swift-accessibility/resources/audit-template.swift +128 -0
  93. package/skills/ios/swift-accessibility/resources/qa-checklist.md +258 -0
  94. package/skills/ios/swift-concurrency/LICENSE +21 -0
  95. package/skills/ios/swift-concurrency/SKILL.md +171 -0
  96. package/skills/ios/swift-concurrency/references/_index.md +50 -0
  97. package/skills/ios/swift-concurrency/references/actors.md +660 -0
  98. package/skills/ios/swift-concurrency/references/async-algorithms.md +847 -0
  99. package/skills/ios/swift-concurrency/references/async-await-basics.md +266 -0
  100. package/skills/ios/swift-concurrency/references/async-sequences.md +710 -0
  101. package/skills/ios/swift-concurrency/references/core-data.md +560 -0
  102. package/skills/ios/swift-concurrency/references/glossary.md +135 -0
  103. package/skills/ios/swift-concurrency/references/linting.md +155 -0
  104. package/skills/ios/swift-concurrency/references/memory-management.md +569 -0
  105. package/skills/ios/swift-concurrency/references/migration.md +1104 -0
  106. package/skills/ios/swift-concurrency/references/performance.md +593 -0
  107. package/skills/ios/swift-concurrency/references/sendable.md +598 -0
  108. package/skills/ios/swift-concurrency/references/tasks.md +636 -0
  109. package/skills/ios/swift-concurrency/references/testing.md +592 -0
  110. package/skills/ios/swift-concurrency/references/threading.md +495 -0
  111. package/skills/ios/swift-security-expert/LICENSE +21 -0
  112. package/skills/ios/swift-security-expert/SKILL.md +470 -0
  113. package/skills/ios/swift-security-expert/references/biometric-authentication.md +565 -0
  114. package/skills/ios/swift-security-expert/references/certificate-trust.md +592 -0
  115. package/skills/ios/swift-security-expert/references/common-anti-patterns.md +690 -0
  116. package/skills/ios/swift-security-expert/references/compliance-owasp-mapping.md +537 -0
  117. package/skills/ios/swift-security-expert/references/credential-storage-patterns.md +721 -0
  118. package/skills/ios/swift-security-expert/references/cryptokit-public-key.md +505 -0
  119. package/skills/ios/swift-security-expert/references/cryptokit-symmetric.md +497 -0
  120. package/skills/ios/swift-security-expert/references/keychain-access-control.md +508 -0
  121. package/skills/ios/swift-security-expert/references/keychain-fundamentals.md +596 -0
  122. package/skills/ios/swift-security-expert/references/keychain-item-classes.md +476 -0
  123. package/skills/ios/swift-security-expert/references/keychain-sharing.md +458 -0
  124. package/skills/ios/swift-security-expert/references/migration-legacy-stores.md +727 -0
  125. package/skills/ios/swift-security-expert/references/secure-enclave.md +539 -0
  126. package/skills/ios/swift-security-expert/references/testing-security-code.md +781 -0
  127. package/skills/ios/swift-testing-expert/LICENSE +21 -0
  128. package/skills/ios/swift-testing-expert/SKILL.md +79 -0
  129. package/skills/ios/swift-testing-expert/references/_index.md +12 -0
  130. package/skills/ios/swift-testing-expert/references/async-testing-and-waiting.md +127 -0
  131. package/skills/ios/swift-testing-expert/references/expectations.md +145 -0
  132. package/skills/ios/swift-testing-expert/references/fundamentals.md +141 -0
  133. package/skills/ios/swift-testing-expert/references/migration-from-xctest.md +127 -0
  134. package/skills/ios/swift-testing-expert/references/parallelization-and-isolation.md +95 -0
  135. package/skills/ios/swift-testing-expert/references/parameterized-testing.md +284 -0
  136. package/skills/ios/swift-testing-expert/references/performance-and-best-practices.md +187 -0
  137. package/skills/ios/swift-testing-expert/references/traits-and-tags.md +114 -0
  138. package/skills/ios/swift-testing-expert/references/xcode-workflows.md +70 -0
  139. package/skills/ios/swiftdata-pro/LICENSE +21 -0
  140. package/skills/ios/swiftdata-pro/SKILL.md +102 -0
  141. package/skills/ios/swiftdata-pro/agents/openai.yaml +10 -0
  142. package/skills/ios/swiftdata-pro/assets/swiftdata-pro-icon.png +0 -0
  143. package/skills/ios/swiftdata-pro/assets/swiftdata-pro-icon.svg +29 -0
  144. package/skills/ios/swiftdata-pro/references/class-inheritance.md +104 -0
  145. package/skills/ios/swiftdata-pro/references/cloudkit.md +10 -0
  146. package/skills/ios/swiftdata-pro/references/core-rules.md +20 -0
  147. package/skills/ios/swiftdata-pro/references/indexing.md +27 -0
  148. package/skills/ios/swiftdata-pro/references/predicates.md +73 -0
  149. package/skills/ios/swiftui-design-principles/AGENTS.md +21 -0
  150. package/skills/ios/swiftui-design-principles/LICENSE +21 -0
  151. package/skills/ios/swiftui-design-principles/README.md +41 -0
  152. package/skills/ios/swiftui-design-principles/SKILL.md +605 -0
  153. package/skills/ios/swiftui-design-principles/metadata.json +10 -0
  154. package/skills/ios/swiftui-liquid-glass/LICENSE +21 -0
  155. package/skills/ios/swiftui-liquid-glass/SKILL.md +95 -0
  156. package/skills/ios/swiftui-liquid-glass/agents/openai.yaml +4 -0
  157. package/skills/ios/swiftui-liquid-glass/references/liquid-glass.md +280 -0
  158. package/skills/ios/swiftui-performance-audit/LICENSE +21 -0
  159. package/skills/ios/swiftui-performance-audit/SKILL.md +111 -0
  160. package/skills/ios/swiftui-performance-audit/agents/openai.yaml +4 -0
  161. package/skills/ios/swiftui-performance-audit/references/code-smells.md +150 -0
  162. package/skills/ios/swiftui-performance-audit/references/demystify-swiftui-performance-wwdc23.md +46 -0
  163. package/skills/ios/swiftui-performance-audit/references/optimizing-swiftui-performance-instruments.md +29 -0
  164. package/skills/ios/swiftui-performance-audit/references/profiling-intake.md +44 -0
  165. package/skills/ios/swiftui-performance-audit/references/report-template.md +47 -0
  166. package/skills/ios/swiftui-performance-audit/references/understanding-hangs-in-your-app.md +33 -0
  167. package/skills/ios/swiftui-performance-audit/references/understanding-improving-swiftui-performance.md +52 -0
  168. package/skills/ios/swiftui-pro/LICENSE +21 -0
  169. package/skills/ios/swiftui-pro/SKILL.md +108 -0
  170. package/skills/ios/swiftui-pro/agents/openai.yaml +10 -0
  171. package/skills/ios/swiftui-pro/assets/swiftui-pro-icon.png +0 -0
  172. package/skills/ios/swiftui-pro/assets/swiftui-pro-icon.svg +29 -0
  173. package/skills/ios/swiftui-pro/references/accessibility.md +13 -0
  174. package/skills/ios/swiftui-pro/references/api.md +39 -0
  175. package/skills/ios/swiftui-pro/references/data.md +43 -0
  176. package/skills/ios/swiftui-pro/references/design.md +31 -0
  177. package/skills/ios/swiftui-pro/references/hygiene.md +9 -0
  178. package/skills/ios/swiftui-pro/references/navigation.md +14 -0
  179. package/skills/ios/swiftui-pro/references/performance.md +46 -0
  180. package/skills/ios/swiftui-pro/references/swift.md +56 -0
  181. package/skills/ios/swiftui-pro/references/views.md +35 -0
  182. package/skills/ios/swiftui-ui-patterns/LICENSE +21 -0
  183. package/skills/ios/swiftui-ui-patterns/SKILL.md +100 -0
  184. package/skills/ios/swiftui-ui-patterns/agents/openai.yaml +4 -0
  185. package/skills/ios/swiftui-ui-patterns/references/app-wiring.md +201 -0
  186. package/skills/ios/swiftui-ui-patterns/references/async-state.md +96 -0
  187. package/skills/ios/swiftui-ui-patterns/references/components-index.md +50 -0
  188. package/skills/ios/swiftui-ui-patterns/references/controls.md +57 -0
  189. package/skills/ios/swiftui-ui-patterns/references/deeplinks.md +66 -0
  190. package/skills/ios/swiftui-ui-patterns/references/focus.md +90 -0
  191. package/skills/ios/swiftui-ui-patterns/references/form.md +97 -0
  192. package/skills/ios/swiftui-ui-patterns/references/grids.md +71 -0
  193. package/skills/ios/swiftui-ui-patterns/references/haptics.md +71 -0
  194. package/skills/ios/swiftui-ui-patterns/references/input-toolbar.md +51 -0
  195. package/skills/ios/swiftui-ui-patterns/references/lightweight-clients.md +93 -0
  196. package/skills/ios/swiftui-ui-patterns/references/list.md +86 -0
  197. package/skills/ios/swiftui-ui-patterns/references/loading-placeholders.md +38 -0
  198. package/skills/ios/swiftui-ui-patterns/references/macos-settings.md +71 -0
  199. package/skills/ios/swiftui-ui-patterns/references/matched-transitions.md +59 -0
  200. package/skills/ios/swiftui-ui-patterns/references/media.md +73 -0
  201. package/skills/ios/swiftui-ui-patterns/references/menu-bar.md +101 -0
  202. package/skills/ios/swiftui-ui-patterns/references/navigationstack.md +159 -0
  203. package/skills/ios/swiftui-ui-patterns/references/overlay.md +45 -0
  204. package/skills/ios/swiftui-ui-patterns/references/performance.md +62 -0
  205. package/skills/ios/swiftui-ui-patterns/references/previews.md +48 -0
  206. package/skills/ios/swiftui-ui-patterns/references/scroll-reveal.md +133 -0
  207. package/skills/ios/swiftui-ui-patterns/references/scrollview.md +87 -0
  208. package/skills/ios/swiftui-ui-patterns/references/searchable.md +71 -0
  209. package/skills/ios/swiftui-ui-patterns/references/sheets.md +155 -0
  210. package/skills/ios/swiftui-ui-patterns/references/split-views.md +72 -0
  211. package/skills/ios/swiftui-ui-patterns/references/tabview.md +114 -0
  212. package/skills/ios/swiftui-ui-patterns/references/theming.md +71 -0
  213. package/skills/ios/swiftui-ui-patterns/references/title-menus.md +93 -0
  214. package/skills/ios/swiftui-ui-patterns/references/top-bar.md +49 -0
  215. package/skills/ios/swiftui-view-refactor/LICENSE +21 -0
  216. package/skills/ios/swiftui-view-refactor/SKILL.md +207 -0
  217. package/skills/ios/swiftui-view-refactor/agents/openai.yaml +4 -0
  218. package/skills/ios/swiftui-view-refactor/references/mv-patterns.md +161 -0
  219. package/skills/ios/widgetkit/LICENSE +131 -0
  220. package/skills/ios/widgetkit/SKILL.md +502 -0
  221. package/skills/ios/widgetkit/references/widgetkit-advanced.md +871 -0
@@ -0,0 +1,585 @@
1
+ # Semantic Structure
2
+
3
+ Covers grouping, reading order, focus management, custom rotors, modal focus trapping, and heading hierarchy — the structural layer of accessibility that determines how assistive technologies navigate and understand your UI.
4
+
5
+ ## Contents
6
+ - [Grouping Elements](#grouping-elements)
7
+ - [Reading Order and Sort Priority](#reading-order-and-sort-priority)
8
+ - [Focus Management](#focus-management)
9
+ - [Custom Rotors](#custom-rotors)
10
+ - [Modal Focus Trapping](#modal-focus-trapping)
11
+ - [Heading Hierarchy](#heading-hierarchy)
12
+ - [UIAccessibilityContainer (UIKit)](#uiaccessibilitycontainer-uikit)
13
+ - [Common Mistakes](#common-mistakes)
14
+
15
+ ---
16
+
17
+ ## Grouping Elements
18
+
19
+ ### SwiftUI: `.accessibilityElement(children:)`
20
+
21
+ Controls how VoiceOver exposes a container's children.
22
+
23
+ **`.combine`** — Merges all descendants into one element. VoiceOver reads their labels in order. Best for related content that forms a single semantic unit.
24
+
25
+ ```swift
26
+ // ✅ Read as "4.5 stars, 2,304 ratings"
27
+ HStack {
28
+ Image(systemName: "star.fill").accessibilityHidden(true)
29
+ Text("4.5")
30
+ Text("(2,304 ratings)")
31
+ }
32
+ .accessibilityElement(children: .combine)
33
+ // Provide an explicit label when auto-combined text isn't natural:
34
+ // .accessibilityLabel("4.5 stars, 2,304 ratings")
35
+
36
+ // ✅ Product card read as a single element
37
+ VStack(alignment: .leading) {
38
+ Text(product.name)
39
+ Text(product.price, format: .currency(code: "USD"))
40
+ Text(product.stock > 0 ? "In stock" : "Out of stock")
41
+ }
42
+ .accessibilityElement(children: .combine)
43
+ ```
44
+
45
+ **`.contain`** — Groups elements, exposes each child individually. Use for containers that need a group label while preserving child navigability (e.g., grouped form sections).
46
+
47
+ ```swift
48
+ // ✅ Group sidebar — users can skip the whole group with Switch Control
49
+ SidebarView()
50
+ .accessibilityElement(children: .contain)
51
+ .accessibilityLabel("Sidebar navigation")
52
+ ```
53
+
54
+ **`.ignore`** — Hides all children from VoiceOver. The container itself becomes the element (or is hidden if no label). Use for purely decorative compositions.
55
+
56
+ ```swift
57
+ // ✅ Decorative separator with text
58
+ HStack {
59
+ Divider()
60
+ Text("OR")
61
+ Divider()
62
+ }
63
+ .accessibilityElement(children: .ignore)
64
+ .accessibilityLabel("Or") // expose the semantic meaning
65
+
66
+ // ✅ Purely decorative — expose nothing
67
+ BackgroundAnimationView()
68
+ .accessibilityHidden(true) // simpler than .ignore for full hiding
69
+ ```
70
+
71
+ ### Explicit Custom Children
72
+
73
+ ```swift
74
+ // Provide a completely custom child list
75
+ .accessibilityChildren {
76
+ ForEach(filteredItems) { item in
77
+ Text(item.title)
78
+ }
79
+ }
80
+ ```
81
+
82
+ ### UIKit: `shouldGroupAccessibilityChildren`
83
+
84
+ Groups all children into a single Switch Control scanning unit. Does NOT merge elements for VoiceOver navigation (VoiceOver still reads each child individually).
85
+
86
+ ```swift
87
+ // ✅ Switch Control: skip entire sidebar with one tap
88
+ sidebarView.shouldGroupAccessibilityChildren = true
89
+ sidebarView.accessibilityLabel = "Sidebar navigation"
90
+
91
+ // ✅ Container exposes ordered children to VoiceOver
92
+ class CardView: UIView {
93
+ override var isAccessibilityElement: Bool {
94
+ get { false } // container is NOT an element
95
+ set { }
96
+ }
97
+ override var accessibilityElements: [Any]? {
98
+ get { [titleLabel, priceLabel, addButton] }
99
+ set { }
100
+ }
101
+ }
102
+ ```
103
+
104
+ ---
105
+
106
+ ## Reading Order and Sort Priority
107
+
108
+ VoiceOver reads elements in the order they appear in the accessibility tree, which usually follows the view hierarchy and layout direction. Override when visual and semantic order differ.
109
+
110
+ ### SwiftUI: `.accessibilitySortPriority(_:)`
111
+
112
+ Higher values are read first. Default is 0. Negative values push elements to the end.
113
+
114
+ ```swift
115
+ // ✅ Ensure critical info is read before decorative content
116
+ VStack {
117
+ Text("Error: Payment failed")
118
+ .accessibilitySortPriority(2) // read first
119
+ Image(systemName: "exclamationmark.circle")
120
+ .accessibilityHidden(true) // decorative
121
+ Text("Please update your payment method")
122
+ .accessibilitySortPriority(1) // read second
123
+ DismissButton()
124
+ .accessibilitySortPriority(-1) // read last
125
+ }
126
+ ```
127
+
128
+ ### SwiftUI: `.accessibilityChildrenInNavigationOrder(_:)`
129
+
130
+ Provides an explicit, ordered list of identifiers for navigation. Use when sort priority alone isn't sufficient.
131
+
132
+ ```swift
133
+ @Namespace var navOrder
134
+
135
+ var body: some View {
136
+ ZStack {
137
+ ContentView()
138
+ .accessibilityElement(id: "content", namespace: navOrder)
139
+ HeaderView()
140
+ .accessibilityElement(id: "header", namespace: navOrder)
141
+ FooterView()
142
+ .accessibilityElement(id: "footer", namespace: navOrder)
143
+ }
144
+ .accessibilityChildrenInNavigationOrder(["header", "content", "footer"], namespace: navOrder)
145
+ }
146
+ ```
147
+
148
+ ### UIKit: `accessibilityElements` Array
149
+
150
+ The order of elements in `accessibilityElements` dictates VoiceOver navigation order.
151
+
152
+ ```swift
153
+ class DashboardView: UIView {
154
+ override var accessibilityElements: [Any]? {
155
+ get {
156
+ // Explicitly control reading order
157
+ [headerView, alertBanner, contentArea, actionButton]
158
+ }
159
+ set { }
160
+ }
161
+ }
162
+ ```
163
+
164
+ ---
165
+
166
+ ## Focus Management
167
+
168
+ ### SwiftUI: `@AccessibilityFocusState`
169
+
170
+ Programmatically move VoiceOver focus to a specific element. Essential when content appears dynamically (modals, error messages, state changes).
171
+
172
+ ```swift
173
+ @AccessibilityFocusState private var isErrorFocused: Bool
174
+
175
+ var body: some View {
176
+ VStack {
177
+ TextField("Email", text: $email)
178
+
179
+ if let error = validationError {
180
+ Text(error)
181
+ .foregroundStyle(.red)
182
+ .accessibilityFocused($isErrorFocused)
183
+ }
184
+
185
+ Button("Submit") {
186
+ if let error = validate() {
187
+ validationError = error
188
+ isErrorFocused = true // VoiceOver jumps to error
189
+ }
190
+ }
191
+ }
192
+ }
193
+ ```
194
+
195
+ ### Multiple Focus Targets
196
+
197
+ ```swift
198
+ enum FormField { case name, email, password }
199
+
200
+ @AccessibilityFocusState private var focusedField: FormField?
201
+
202
+ var body: some View {
203
+ VStack {
204
+ TextField("Name", text: $name)
205
+ .accessibilityFocused($focusedField, equals: .name)
206
+ TextField("Email", text: $email)
207
+ .accessibilityFocused($focusedField, equals: .email)
208
+ SecureField("Password", text: $password)
209
+ .accessibilityFocused($focusedField, equals: .password)
210
+
211
+ Button("Next") {
212
+ // Move focus programmatically
213
+ switch focusedField {
214
+ case .name: focusedField = .email
215
+ case .email: focusedField = .password
216
+ default: submit()
217
+ }
218
+ }
219
+ }
220
+ }
221
+ ```
222
+
223
+ ### `.accessibilityDefaultFocus(_:_:)` — Initial Focus (iOS 17+)
224
+
225
+ Sets which element receives focus when a view first appears.
226
+
227
+ ```swift
228
+ @AccessibilityFocusState private var isDefaultFocused: Bool
229
+
230
+ AlertView()
231
+ .onAppear { isDefaultFocused = true }
232
+
233
+ // Within AlertView:
234
+ Button("Confirm") { confirm() }
235
+ .accessibilityDefaultFocus($isDefaultFocused, true)
236
+ ```
237
+
238
+ ### UIKit: Moving Focus with Notifications
239
+
240
+ ```swift
241
+ // Focus moves to a specific element (partial update)
242
+ UIAccessibility.post(notification: .layoutChanged, argument: errorLabel)
243
+
244
+ // Focus resets to start of new screen (full replacement)
245
+ UIAccessibility.post(notification: .screenChanged, argument: firstInteractiveElement)
246
+
247
+ // Announce a change without moving focus
248
+ UIAccessibility.post(notification: .announcement, argument: "Message sent")
249
+ ```
250
+
251
+ **When to use each:**
252
+
253
+ | Notification | Use When |
254
+ |---|---|
255
+ | `.layoutChanged` | Part of the screen changes (row inserted, error appears, section expands) |
256
+ | `.screenChanged` | Entire content changes (modal appears, tab switched, navigation push) |
257
+ | `.announcement` | Background state change — no layout change occurred |
258
+ | `.pageScrolled` | Custom scroll view changed pages |
259
+
260
+ ---
261
+
262
+ ## Custom Rotors
263
+
264
+ The VoiceOver rotor (two-finger rotation gesture) lets users jump between elements of a specific type. Custom rotors add app-specific navigation shortcuts.
265
+
266
+ ### SwiftUI: `accessibilityRotor(_:entries:)`
267
+
268
+ ```swift
269
+ // ✅ Jump between unread messages
270
+ .accessibilityRotor("Unread Messages") {
271
+ ForEach(messages.filter(\.isUnread)) { message in
272
+ AccessibilityRotorEntry(message.preview, id: message.id)
273
+ }
274
+ }
275
+
276
+ // ✅ Jump between search result matches in a document
277
+ Text(documentText)
278
+ .accessibilityRotor("Search Results") {
279
+ ForEach(searchHighlights) { match in
280
+ AccessibilityRotorEntry(match.text, textRange: match.range)
281
+ }
282
+ }
283
+ ```
284
+
285
+ ### Rotor with Explicit Focus Targeting
286
+
287
+ ```swift
288
+ @Namespace var headingNamespace
289
+
290
+ var body: some View {
291
+ ScrollView {
292
+ ForEach(article.sections) { section in
293
+ VStack(alignment: .leading) {
294
+ Text(section.heading)
295
+ .font(.title2)
296
+ .accessibilityAddTraits(.isHeader)
297
+ .accessibilityRotorEntry(id: section.id, in: headingNamespace)
298
+
299
+ Text(section.body)
300
+ }
301
+ }
302
+ }
303
+ .accessibilityRotor("Headings", entries: article.sections, id: \.id, in: headingNamespace, label: \.heading)
304
+ }
305
+ ```
306
+
307
+ ### UIKit: `UIAccessibilityCustomRotor`
308
+
309
+ ```swift
310
+ class ArticleViewController: UIViewController {
311
+ var headings: [Heading] = []
312
+
313
+ override func viewDidLoad() {
314
+ super.viewDidLoad()
315
+ accessibilityCustomRotors = [makeHeadingRotor()]
316
+ }
317
+
318
+ private func makeHeadingRotor() -> UIAccessibilityCustomRotor {
319
+ UIAccessibilityCustomRotor(name: "Headings") { [weak self] predicate in
320
+ guard let self = self else { return nil }
321
+
322
+ // Find current heading index
323
+ let currentIndex = self.headings.firstIndex {
324
+ $0.view === predicate.currentItem.targetElement as? UIView
325
+ }
326
+
327
+ let nextIndex: Int
328
+ switch predicate.searchDirection {
329
+ case .next:
330
+ nextIndex = (currentIndex.map { $0 + 1 }) ?? 0
331
+ case .previous:
332
+ nextIndex = (currentIndex.map { $0 - 1 }) ?? (self.headings.count - 1)
333
+ @unknown default:
334
+ return nil
335
+ }
336
+
337
+ guard nextIndex >= 0, nextIndex < self.headings.count else { return nil }
338
+ let heading = self.headings[nextIndex]
339
+ return UIAccessibilityCustomRotorItemResult(targetElement: heading.view, targetRange: nil)
340
+ }
341
+ }
342
+ }
343
+ ```
344
+
345
+ ---
346
+
347
+ ## Modal Focus Trapping
348
+
349
+ When a modal, alert, or sheet appears, VoiceOver must stay within the modal. Users should not be able to swipe to background content.
350
+
351
+ ### SwiftUI
352
+
353
+ SwiftUI `.sheet()`, `.alert()`, `.confirmationDialog()`, and `NavigationStack` modals handle focus trapping automatically.
354
+
355
+ ```swift
356
+ // ✅ Focus trapped automatically
357
+ .sheet(isPresented: $showSettings) {
358
+ SettingsView() // VoiceOver stays within this view
359
+ }
360
+
361
+ // ✅ Post notification after manual presentation
362
+ .onChange(of: showCustomModal) { _, isShowing in
363
+ if isShowing {
364
+ // Give SwiftUI a moment to render, then move focus
365
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
366
+ AccessibilityNotification.ScreenChanged().post()
367
+ }
368
+ }
369
+ }
370
+ ```
371
+
372
+ ### UIKit: `accessibilityViewIsModal`
373
+
374
+ ```swift
375
+ class CustomModalView: UIView {
376
+ // Set this on the OUTERMOST modal view, not a child
377
+ override var accessibilityViewIsModal: Bool {
378
+ get { true }
379
+ set { }
380
+ }
381
+ }
382
+
383
+ func presentModal() {
384
+ let modal = CustomModalView()
385
+ view.addSubview(modal)
386
+
387
+ // VoiceOver focus moves into the modal
388
+ UIAccessibility.post(notification: .screenChanged, argument: modal)
389
+ // VoiceOver now ignores all views behind the modal
390
+ }
391
+
392
+ // ✅ Support Escape: two-finger Z gesture (VoiceOver) / Escape key (Full Keyboard Access)
393
+ override func accessibilityPerformEscape() -> Bool {
394
+ dismissModal()
395
+ return true
396
+ }
397
+ ```
398
+
399
+ **Critical pitfalls:**
400
+ - Setting `accessibilityViewIsModal` on a child view (not the container) — focus escapes to siblings
401
+ - Forgetting `accessibilityPerformEscape` — users cannot dismiss with VoiceOver gestures
402
+ - Not posting `.screenChanged` after presentation — VoiceOver stays focused on background
403
+
404
+ ### Focus Return on Dismissal
405
+
406
+ ```swift
407
+ // UIKit — return focus to the trigger after dismissing
408
+ @AccessibilityFocusState private var returnFocus: Bool // SwiftUI equivalent
409
+
410
+ // UIKit: track the element that opened the modal
411
+ weak var presentingElement: UIView?
412
+
413
+ func dismissModal() {
414
+ dismiss(animated: true) {
415
+ // Return focus to the element that triggered the modal
416
+ UIAccessibility.post(notification: .screenChanged, argument: self.presentingElement)
417
+ }
418
+ }
419
+ ```
420
+
421
+ ---
422
+
423
+ ## Heading Hierarchy
424
+
425
+ Headings let VoiceOver users navigate with the Headings rotor — jumping directly between sections.
426
+
427
+ ### SwiftUI
428
+
429
+ ```swift
430
+ // ✅ Mark section headers
431
+ Text("Account Settings")
432
+ .font(.title2)
433
+ .accessibilityAddTraits(.isHeader)
434
+
435
+ // ✅ Heading with level (iOS 17+)
436
+ Text("Chapter 1: Introduction")
437
+ .accessibilityAddTraits(.isHeader)
438
+ .accessibilityHeading(.h1)
439
+
440
+ Text("1.1 Getting Started")
441
+ .accessibilityAddTraits(.isHeader)
442
+ .accessibilityHeading(.h2)
443
+
444
+ // Available levels: .h1, .h2, .h3, .h4, .h5, .h6, .unspecified
445
+ ```
446
+
447
+ ### UIKit
448
+
449
+ ```swift
450
+ // ✅ Header trait
451
+ sectionLabel.accessibilityTraits = [.header, .staticText]
452
+
453
+ // Heading levels are set via accessibilityHeading (tvOS/Mac) or traits on iOS
454
+ // iOS doesn't expose explicit heading levels through UIKit APIs —
455
+ // use accessibilityTraits = .header for all heading levels
456
+ ```
457
+
458
+ ### Document Structure Pattern
459
+
460
+ For document-like content (articles, settings pages, help content):
461
+
462
+ ```swift
463
+ struct ArticleView: View {
464
+ var article: Article
465
+
466
+ var body: some View {
467
+ ScrollView {
468
+ VStack(alignment: .leading, spacing: 24) {
469
+ // Page title — h1
470
+ Text(article.title)
471
+ .font(.largeTitle)
472
+ .accessibilityAddTraits(.isHeader)
473
+ .accessibilityHeading(.h1)
474
+
475
+ ForEach(article.sections) { section in
476
+ // Section heading — h2
477
+ Text(section.title)
478
+ .font(.title2)
479
+ .accessibilityAddTraits(.isHeader)
480
+ .accessibilityHeading(.h2)
481
+
482
+ Text(section.body)
483
+
484
+ ForEach(section.subsections) { sub in
485
+ // Subsection heading — h3
486
+ Text(sub.title)
487
+ .font(.headline)
488
+ .accessibilityAddTraits(.isHeader)
489
+ .accessibilityHeading(.h3)
490
+
491
+ Text(sub.body)
492
+ }
493
+ }
494
+ }
495
+ .padding()
496
+ }
497
+ }
498
+ }
499
+ ```
500
+
501
+ ---
502
+
503
+ ## UIAccessibilityContainer (UIKit)
504
+
505
+ For complex views where VoiceOver needs structured navigation within a single UIView.
506
+
507
+ ### `accessibilityContainerType`
508
+
509
+ Provides semantic meaning to the container. VoiceOver announces entering/leaving based on type.
510
+
511
+ ```swift
512
+ // Table container — VoiceOver says "table" when entering
513
+ tableContainerView.accessibilityContainerType = .dataTable
514
+
515
+ // List container — VoiceOver says "list"
516
+ listView.accessibilityContainerType = .list
517
+
518
+ // Landmark — like HTML landmarks
519
+ navContainerView.accessibilityContainerType = .landmark
520
+
521
+ // Semantic group — related content without a specific type
522
+ cardView.accessibilityContainerType = .semanticGroup
523
+ ```
524
+
525
+ ### `accessibilityNavigationStyle`
526
+
527
+ Controls how VoiceOver navigates within a container.
528
+
529
+ ```swift
530
+ // .combined — children are navigated as a group (one element)
531
+ container.accessibilityNavigationStyle = .combined
532
+
533
+ // .separate — children are navigated individually (default for most containers)
534
+ container.accessibilityNavigationStyle = .separate
535
+
536
+ // .automatic — system chooses (default)
537
+ container.accessibilityNavigationStyle = .automatic
538
+ ```
539
+
540
+ ### Custom Element Ordering
541
+
542
+ ```swift
543
+ class DashboardView: UIView {
544
+ @IBOutlet var alertBanner: UIView!
545
+ @IBOutlet var header: UIView!
546
+ @IBOutlet var mainContent: UIView!
547
+ @IBOutlet var actionButtons: UIView!
548
+
549
+ override var isAccessibilityElement: Bool {
550
+ get { false } // Container is NOT itself an element
551
+ set { }
552
+ }
553
+
554
+ override var accessibilityElements: [Any]? {
555
+ get {
556
+ // Alert banner first — critical content
557
+ var elements: [Any] = []
558
+ if alertBanner.isHidden == false {
559
+ elements.append(alertBanner!)
560
+ }
561
+ elements.append(contentsOf: [header!, mainContent!, actionButtons!])
562
+ return elements
563
+ }
564
+ set { }
565
+ }
566
+ }
567
+ ```
568
+
569
+ ---
570
+
571
+ ## Common Mistakes
572
+
573
+ | Mistake | Fix |
574
+ |---|---|
575
+ | Container `isAccessibilityElement = true` AND `accessibilityElements` set | Set `isAccessibilityElement = false` on containers that expose children |
576
+ | `.accessibilityElement(children: .combine)` nested inside another `.combine` | Flatten the structure — only one combine level |
577
+ | VoiceOver reads background content behind modal | Set `accessibilityViewIsModal = true` on the outermost modal view |
578
+ | No focus movement after modal appears | Post `.screenChanged` notification pointing to modal's first element |
579
+ | Reading order follows visual layout instead of semantic order | Use `.accessibilitySortPriority` or `accessibilityElements` to control order |
580
+ | No custom rotor for data-rich lists | Add `accessibilityRotor` for efficient navigation of long lists |
581
+ | Missing `.isHeader` trait on section headings | Every section title should have `.accessibilityAddTraits(.isHeader)` |
582
+ | No `accessibilityPerformEscape()` on custom modals | Implement to support two-finger Z gesture and Escape key |
583
+ | `accessibilityViewIsModal` on a child, not the root | Must be on the outermost modal container view |
584
+ | Focus lost after content update (async data loads) | Post `.layoutChanged` with the first new element as argument |
585
+ | `accessibilityElements` not invalidated after data change | Nil-out cache + post `.layoutChanged` when underlying data changes |