DEV Community

Sebastien Lato
Sebastien Lato

Posted on

SwiftUI Accessibility (Production-Grade, Real World Patterns)

Accessibility is not a checklist.

It’s not:

  • “add one modifier”
  • “turn on VoiceOver once”
  • “increase font size and hope”

Accessibility is architecture + intent + respect for system settings.

This post shows how to build production-grade accessibility in SwiftUI — the kind Apple expects and real users rely on.


🧠 The Core Accessibility Rule

If your app works only for you, it’s broken.

Accessibility must work for:

  • VoiceOver users
  • Dynamic Type users
  • Reduce Motion users
  • Switch Control users
  • Keyboard users
  • Cognitive load constraints

SwiftUI gives you the tools — but you must use them correctly.


🧩 1. Accessibility Is About Meaning, Not Views

This is bad:

Image(systemName: "heart")
Enter fullscreen mode Exit fullscreen mode

This is correct:

Image(systemName: "heart")
    .accessibilityLabel("Like")
Enter fullscreen mode Exit fullscreen mode

Better:

Button {
    like()
} label: {
    Image(systemName: "heart")
}
.accessibilityLabel("Like post")
Enter fullscreen mode Exit fullscreen mode

Rule:
📌 Screen readers read meaning, not layout.


🔤 2. Dynamic Type Is Non-Negotiable

Never lock font sizes.

❌ Bad:

.font(.system(size: 14))
Enter fullscreen mode Exit fullscreen mode

✅ Good:

.font(.body)
Enter fullscreen mode Exit fullscreen mode

If you must scale:

.font(.system(.body, design: .rounded))
.dynamicTypeSize(.small ... .accessibility3)
Enter fullscreen mode Exit fullscreen mode

Test with Accessibility XXL — always.


🔍 3. Grouping Content Correctly

Without grouping, VoiceOver reads everything.

VStack {
    Text("Balance")
    Text("$1,250")
}
.accessibilityElement(children: .combine)
Enter fullscreen mode Exit fullscreen mode

This becomes:
“Balance, $1,250”

Instead of two unrelated reads.


🎯 4. Accessibility Traits Matter

Traits give context:

.accessibilityAddTraits(.isButton)
Enter fullscreen mode Exit fullscreen mode
.accessibilityAddTraits(.isHeader)
Enter fullscreen mode Exit fullscreen mode

Use headers to make long screens navigable.


🧭 5. Focus Order & Navigation

SwiftUI reads views in layout order.

If the order feels wrong visually → it’s wrong for accessibility.

Fix with:

.accessibilitySortPriority(1)
Enter fullscreen mode Exit fullscreen mode

Higher number = read earlier.

Use sparingly.


🎛️ 6. Reduce Motion Support

Respect user settings.

@Environment(\.accessibilityReduceMotion) var reduceMotion
Enter fullscreen mode Exit fullscreen mode
withAnimation(reduceMotion ? nil : .spring()) {
    expanded.toggle()
}
Enter fullscreen mode Exit fullscreen mode

If animations are decorative → disable them.


🧱 7. Buttons Must Be Big Enough

Apple guideline:

  • 44×44 pt minimum

SwiftUI helps, but custom components often break this.

.contentShape(Rectangle())
.padding()
Enter fullscreen mode Exit fullscreen mode

This improves both:

  • touch
  • accessibility hit testing

🧪 8. Testing Accessibility Properly

Do this every time:

  • Enable VoiceOver
  • Navigate without looking
  • Increase Dynamic Type
  • Enable Reduce Motion
  • Enable Bold Text
  • Enable Increase Contrast

If something feels annoying → it is.


🚨 9. Accessible Errors & Alerts

Bad:

Text("Error")
Enter fullscreen mode Exit fullscreen mode

Good:

Text(error.message)
    .accessibilityLabel("Error: \(error.message)")
Enter fullscreen mode Exit fullscreen mode

For critical errors:

  • use alerts
  • use announcements if needed
UIAccessibility.post(
    notification: .announcement,
    argument: "Upload failed"
)
Enter fullscreen mode Exit fullscreen mode

🧠 10. Accessibility Is Architecture

Accessibility breaks when:

  • state is unclear
  • labels are dynamic but not updated
  • components are reused incorrectly
  • views are visually clever but semantically empty

If your data flow is clean, accessibility is easier.


🚀 Final Thoughts

Accessibility is not extra work.

It’s:

  • better UX
  • clearer UI
  • fewer bugs
  • better architecture
  • a better app

SwiftUI makes accessibility possible by default — but only if you design with intent.

Top comments (0)