iOS Accessibility Checklist for the EAA
The European Accessibility Act (EAA) has been mandatory since 28 June 2025 for banking, e-commerce, transport, media and other consumer-facing digital services. National transpositions carry serious penalties — in Spain, the sanctions regime under Ley 11/2023, by reference to Real Decreto Legislativo 1/2013, allows fines of up to one million euros. And no, the famous "grace period until 2030" does not exist for your app: that transitional provision only covers products already in legal use before the deadline.
The technical standard behind the EAA is EN 301 549, which for software relies on the WCAG 2.1 level AA criteria. The catch: WCAG is written with the web in mind, and translating it to a native iOS app — UIKit or SwiftUI — is precisely the part almost nobody documents.
This checklist does that translation. It is 27 verifiable points, organised into six blocks, each with its WCAG criterion where one applies and a hint at the iOS API involved. Use it to self-assess your app before commissioning an audit, or as a control list during development.
Published: · Updated:
How to use this checklist
Every point is a statement your app either meets or does not; there is deliberately no middle ground. Walk through the six blocks with the app in hand, VoiceOver on and text size at maximum. Anything that fails is a real finding an auditor — or a user — will also hit.
This checklist covers what can be verified without real users. It does not replace a full audit: context-dependent criteria, testing with assistive-technology users and the accessibility statement fall outside it — and we explain why at the end of the page.
| Block | WCAG criteria | iOS APIs involved |
|---|---|---|
| VoiceOver and semantic labelling | 1.1.1 · 1.3.2 · 4.1.2 · 4.1.3 | accessibilityLabel, accessibilityTraits, UIAccessibility.post |
| Text and Dynamic Type | 1.4.4 · 1.4.5 | preferredFont(forTextStyle:), dynamicTypeSize |
| Colour and contrast | 1.4.1 · 1.4.3 · 1.4.11 | Asset Catalog (contrast variants), accessibilityContrast (UIKit) / colorSchemeContrast (SwiftUI) |
| Touch interaction and gestures | 1.3.4 · 2.1.1 · 2.4.7 · 2.5.1 · 2.5.4 | accessibilityCustomActions, Full Keyboard Access, focusable() |
| Forms and transactional flows | 1.3.5 · 2.2.1 · 3.3.1 · 3.3.2 · 3.3.3 | textContentType, keyboardType, error announcements |
| Media, motion and dynamic content | 1.2.2 · 1.2.5 · 2.3.1 · 2.3.3 | AVPlayer (captions), isReduceMotionEnabled |
VoiceOver and semantic labelling
VoiceOver is the iOS screen reader and the first filter in any audit. If an element is unlabelled, carries the wrong role or reads in a nonsensical order, the app is unusable for a blind person — no matter how polished everything else is.
-
Every interactive element exposes a concise, descriptive accessibility label; no functional image is announced as just "image" or by its asset name.
(WCAG 1.1.1)
accessibilityLabelin UIKit;.accessibilityLabel(_:)in SwiftUI. Decorative images are hidden withisAccessibilityElement = false/.accessibilityHidden(true). -
Every element announces its true role and state: buttons announce as buttons, selected things as selected, disabled things as dimmed.
(WCAG 4.1.2)
accessibilityTraits(.button,.selected,.notEnabled);.accessibilityAddTraits(_:)in SwiftUI. -
The VoiceOver reading order follows the visual and functional logic of the screen, not the accidental order of the view hierarchy.
(WCAG 1.3.2)
accessibilityElementson the container;.accessibilitySortPriority(_:)in SwiftUI. -
Elements that form one unit of meaning (a cell's icon + title + subtitle) are grouped and read as a single element, not three separate stops.
shouldGroupAccessibilityChildren/ container element with a composed label;.accessibilityElement(children: .combine)in SwiftUI. -
Dynamic content changes (loaded results, errors, on-screen updates) are announced to VoiceOver without the user having to hunt for them.
(WCAG 4.1.3)
UIAccessibility.post(notification: .announcement, …)/.layoutChanged;AccessibilityNotification.Announcementin SwiftUI. -
Actions available through contextual gestures (swipe to delete, long press) are also exposed to the rotor as custom actions.
accessibilityCustomActions;.accessibilityAction(named:)in SwiftUI.
Text and Dynamic Type
Dynamic Type lets users choose their text size system-wide. An app that ignores it — or breaks when it is enabled — excludes a huge share of its users, and it is among the first things any auditor checks because it takes seconds to detect.
-
All text in the app scales with Dynamic Type; there are no fixed font sizes on content screens.
(WCAG 1.4.4)
UIFont.preferredFont(forTextStyle:)+adjustsFontForContentSizeCategory = true; SwiftUI'sFontstyles (.body,.headline…) scale by default. - No informational text is rasterised inside images; all text is real text that scales and that VoiceOver can read. (WCAG 1.4.5) compose text + image in the view instead of shipping assets with baked-in text.
-
With text enlarged, layouts grow instead of breaking: nothing truncates with "…" losing information, nothing overlaps, and cell heights are self-sizing.
Auto Layout without fixed heights,
UITableView.automaticDimension; in SwiftUI, avoid restrictive.lineLimitand rigid frames. -
The app remains functional at the accessibility sizes (AX1–AX5), not just the five standard ones; where a horizontal layout stops fitting, it reflows vertically.
traitCollection.preferredContentSizeCategory.isAccessibilityCategory;@Environment(\.dynamicTypeSize)andViewThatFitsin SwiftUI.
Colour and contrast
Insufficient contrast is the most common failure and the cheapest to fix. And colour can never be the sole carrier of meaning: what reads as "red = error" to you is simply another grey to someone with colour blindness.
- Normal text reaches a minimum contrast of 4.5:1 against its background, and large text and UI components reach at least 3:1 — in dark mode too. (WCAG 1.4.3 · 1.4.11) system semantic colours or Asset Catalog with light/dark variants; verify with Xcode's Accessibility Inspector.
- No state or information is conveyed by colour alone: errors, selection and active states also carry an icon, text or shape change. (WCAG 1.4.1) design review; SF Symbols as non-colour reinforcement.
-
The app responds to the system contrast settings and does not break under Smart Invert: photos and already-dark content are not inverted.
high-contrast variants in the Asset Catalog,
UIAccessibility.isDarkerSystemColorsEnabledortraitCollection.accessibilityContrastin UIKit;accessibilityIgnoresInvertColorsand@Environment(\.colorSchemeContrast)in SwiftUI.
Touch interaction and gestures
A control existing does not mean it can be used. Tiny touch targets, gestures that demand two hands or surgical precision, and screens that only respond to shaking the phone are direct barriers for people with reduced mobility.
-
Every touch target measures at least 44×44 points, even when its visual representation is smaller.
extend the area with insets; in UIKit, override
point(inside:with:)orhitTest(_:with:)if needed; Apple's HIG as the reference. -
Every function triggered by a complex or multipoint gesture (pinch, drag, two-finger swipe) has a single-tap alternative.
(WCAG 2.5.1)
visible alternative buttons or
accessibilityCustomActions. -
No function depends on moving the device (shake, tilt) without an on-screen alternative, and motion responses can be disabled.
(WCAG 2.5.4)
UI alternative to
CMMotionManager/ the shake gesture; honour the system settings. -
The app is fully operable with Full Keyboard Access and Switch Control: everything interactive receives focus, focus is visible, and there are no focus traps.
(WCAG 2.1.1 · 2.4.7)
test with Full Keyboard Access on;
UIKeyCommand,canBecomeFocused;.focusable()and@FocusStatein SwiftUI. -
The app works in both portrait and landscape; it does not lock to a single orientation unless that is essential to the task (capturing a cheque, for example).
(WCAG 1.3.4)
do not restrict
supportedInterfaceOrientationswithout justification; in SwiftUI, layouts that adapt to the available size rather than assuming one orientation.
Forms and transactional flows
The EAA is above all about services users do things with: buying, contracting, paying, booking. A flawless onboarding is worth little if the payment form is impassable with VoiceOver. This block decides whether your app complies where it actually matters.
-
Every field has a visible, persistent label; the placeholder is never the only label, because it disappears the moment the user types.
(WCAG 3.3.2)
visible associated label +
accessibilityLabelon the field; in SwiftUI,TextFieldwithLabeledContentor an explicit label. -
When validation fails, the error is identified and described in text next to the affected field, and VoiceOver announces it at that moment.
(WCAG 3.3.1)
error message as text tied to the field +
UIAccessibility.post(notification: .announcement, …). - Recoverable errors offer a concrete correction suggestion ("the IBAN must have 24 characters"), not a generic "invalid data". (WCAG 3.3.3) rule-specific validation messages.
-
Every field declares its purpose and its keyboard: email brings up the email keyboard and autofills, the SMS code suggests itself, passwords work with password managers.
(WCAG 1.3.5)
textContentType(.emailAddress,.oneTimeCode,.password…) +keyboardType. - No step of the transactional flow times out without warning and a way to extend it, and the entire flow — cart to confirmation — is operable end to end with VoiceOver alone. (WCAG 2.2.1) warnings before session expiry with an extend option; manual end-to-end VoiceOver test of the whole flow.
Media, motion and dynamic content
Audiovisual content and animation have rules of their own: what cannot be heard needs captions, what cannot be seen needs description, and motion that looks elegant to you can — literally — make some of your users sick.
-
All pre-recorded video content with audio offers captions, and the player honours the system's caption and styling settings.
(WCAG 1.2.2)
caption tracks in the asset;
AVPlayerhandles them with the user's settings (MACaptionAppearance). -
Pre-recorded video with audio offers audio description wherever visual information is not already carried by the soundtrack; at level AA a transcript does not replace audio description.
(WCAG 1.2.5)
audio description track in the asset (
AVMediaCharacteristic.describesVideoForAccessibility); an accessible transcript is a complement, not the substitute. -
The app honours the "Reduce Motion" setting: parallax, zooms and large transitions are replaced with fades or removed.
(WCAG 2.3.3)
Recommended
UIAccessibility.isReduceMotionEnabled;@Environment(\.accessibilityReduceMotion)in SwiftUI. - Nothing in the app flashes more than three times per second — including videos, loading animations and promotional effects. (WCAG 2.3.1) review of animations and content; no specific API — this is a design rule.
What this checklist does not cover (and when you need an audit)
Being honest here is part of the job: passing these 27 points means you have removed the most frequent, objective barriers — not that your app "complies with the EAA". There are three things a checklist cannot give you:
- Testing with real assistive-technology users — a reading order can be technically correct and still be exhausting to use; only someone who navigates with VoiceOver daily will reveal that.
- The criteria that depend on your app's context — what "large text" means in your design, which alternative makes sense for your gesture, which flows are services covered by the EAA and which are not.
- The formal side of compliance — the documented assessment against EN 301 549 and the accessibility information the service must make publicly available.
Does your app pass the checklist… or not quite?
We run accessibility audits built specifically for native iOS — not web audits adapted after the fact — with remediation included if you need it. From an initial diagnosis to ongoing support, with published pricing.
See the iOS Accessibility (EAA) serviceFrequently asked questions
Let's talk about your iOS challenge
Book 30 minutes with Francisco José García Navarro, Senior iOS Architect, to review your project. No obligation.