Accessibility Inspector: Running a Full Audit of Your iOS App
You ship an app with accessibility modifiers on every view. Labels are set, traits are assigned, the VoiceOver simulator sounds correct on your test device. Then a real user with low vision files a bug: the contrast ratio on your “Watch Ratatouille Tonight” button is 2.8:1 against its background — well below the WCAG 2.1 AA minimum of 4.5:1. You never noticed because your eyes compensated.
Accessibility Inspector catches these issues systematically. It is a free tool bundled with Xcode that performs automated audits, live tree inspection, color contrast validation, and produces exportable reports. This post walks through using it as a first-class part of your development workflow, not as an afterthought before App Store submission.
Contents
- Contents
- The Problem
- Launching and Navigating Accessibility Inspector
- Running an Automated Audit
- Inspecting the Live Accessibility Tree
- WCAG 2.1 Color Contrast Checking
- Exporting Audit Reports
- Integrating Audits into Your CI Pipeline
- Performance Considerations
- When to Use (and When Not To)
- Summary
The Problem
Manual VoiceOver testing catches navigation flow issues but misses systematic problems. Consider this SwiftUI view for a Pixar movie detail screen:
import SwiftUI
struct MovieDetailView: View {
let movie: PixarMovie
var body: some View {
ScrollView {
VStack(alignment: .leading, spacing: 16) {
Image(movie.posterName)
.resizable()
.aspectRatio(contentMode: .fill)
// Missing: .accessibilityLabel
Text(movie.title)
.font(.largeTitle)
.foregroundStyle(Color(hex: "#A0A0A0"))
// Light grey on white: contrast ratio ~2.5:1
HStack {
ForEach(1...5, id: \.self) { star in
Image(systemName: star <= movie.rating
? "star.fill" : "star")
.foregroundStyle(.yellow)
}
// Missing: .accessibilityElement(children: .combine)
// VoiceOver reads 5 separate "star" elements
}
Text(movie.synopsis)
.font(.body)
Button("Watch Tonight") {
// action
}
.foregroundStyle(Color(hex: "#7B7B7B"))
.background(Color(hex: "#E8E8E8"))
// Button text contrast: ~2.3:1
}
}
}
}
This view has at least four accessibility issues: a missing image label, two low-contrast text elements, and a star rating that reads as five separate elements instead of one semantic unit. A manual VoiceOver pass might catch the missing label and the redundant star announcements, but it will not quantify the contrast ratios. Accessibility Inspector catches all four in a single automated scan.
Launching and Navigating Accessibility Inspector
Accessibility Inspector ships with Xcode and lives in the developer tools directory. There are two ways to open it:
From Xcode: Open the Xcode menu > Open Developer Tool > Accessibility Inspector.
From Spotlight: Search for “Accessibility Inspector” — it is indexed as a standalone application.
The Inspector window has three primary components:
-
Target Selector (top bar) — Choose which device or simulator to inspect. The dropdown lists all connected devices and running simulators. Select your target before inspecting.
-
Inspection Panel (center) — Shows the accessibility properties of the currently selected element: label, value, traits, frame, actions, and the element’s position in the accessibility hierarchy.
-
Audit Panel (right sidebar) — The automated audit runner. One click scans the entire visible screen for issues.
Tip: Keep Accessibility Inspector running alongside the Simulator during development. The hover inspection mode updates in real time as you interact with your app, making it trivial to spot missing labels during active development.
Running an Automated Audit
The audit feature is Accessibility Inspector’s most powerful capability. It scans every element on the current screen against Apple’s accessibility guidelines and WCAG criteria.
Running the Audit
- Launch your app in the Simulator (or on a connected device).
- Navigate to the screen you want to audit.
- In Accessibility Inspector, select the correct target from the dropdown.
- Click the Audit button (the checkmark icon in the toolbar) or press Cmd+Shift+A.
- Click Run Audit.
The audit runs in seconds and produces a categorized list of issues.
Understanding Audit Results
Each issue includes:
- Issue Type — Description of the violation (e.g., “Element has no description,” “Potentially inaccessible text,” “Hit target is too small”).
- Affected Element — The specific UI element with the problem, highlighted on screen when selected.
- Suggested Fix — A concrete recommendation for resolving the issue.
For our Pixar movie detail screen, the audit would flag:
| Issue | Element | Category |
|---|---|---|
| Element has no description | Movie poster Image | Missing Label |
| Potentially inaccessible text | Movie title Text | Color Contrast |
| Potentially inaccessible text | ”Watch Tonight” Button | Color Contrast |
| Hit area may be too small | Individual star Image | Hit Target |
Fixing the Flagged Issues
Here is the corrected version of the movie detail view:
struct MovieDetailView: View {
let movie: PixarMovie
var body: some View {
ScrollView {
VStack(alignment: .leading, spacing: 16) {
Image(movie.posterName)
.resizable()
.aspectRatio(contentMode: .fill)
.accessibilityLabel(
"Movie poster for \(movie.title)"
) // ← Fix: added label
Text(movie.title)
.font(.largeTitle)
.foregroundStyle(.primary)
// ← Fix: system color with sufficient contrast
HStack {
ForEach(1...5, id: \.self) { star in
Image(systemName: star <= movie.rating
? "star.fill" : "star")
.foregroundStyle(.yellow)
}
}
.accessibilityElement(children: .ignore)
.accessibilityLabel(
"\(movie.rating) out of 5 stars"
) // ← Fix: single semantic element
.accessibilityValue("\(movie.title) rating")
Text(movie.synopsis)
.font(.body)
Button("Watch Tonight") {
// action
}
.foregroundStyle(.white)
.padding(.horizontal, 24)
.padding(.vertical, 12) // ← Fix: adequate tap target
.background(Color.accentColor) // ← Fix: high contrast
.clipShape(RoundedRectangle(cornerRadius: 8))
}
}
}
}
After applying fixes, re-run the audit to confirm all issues are resolved. The audit should return zero warnings for this screen.
Apple Docs:
accessibilityLabel(_:)— SwiftUI
Inspecting the Live Accessibility Tree
The audit catches broad issues, but the live inspector reveals exactly what assistive technologies see for each element. This is essential for verifying that your custom accessibility implementations behave correctly.
Hover Inspection Mode
- Click the Inspection button (crosshair icon) in the toolbar.
- Hover over any element in the Simulator.
- The Inspector panel updates in real time, showing:
- Label — What VoiceOver reads aloud.
- Value — The current value (for controls like sliders).
- Traits — Semantic traits (button, header, image, etc.).
- Frame — The element’s bounding rectangle.
- Actions — Available actions (activate, increment, scroll).
Validating Custom Accessibility Actions
For a Pixar movie rating slider, the inspector shows whether your custom actions are correctly exposed:
struct MovieRatingControl: View {
@Binding var rating: Int
let movieTitle: String
var body: some View {
HStack {
ForEach(1...5, id: \.self) { star in
Image(systemName: star <= rating
? "star.fill" : "star")
.foregroundStyle(.yellow)
.onTapGesture { rating = star }
}
}
.accessibilityElement()
.accessibilityLabel("Rating for \(movieTitle)")
.accessibilityValue("\(rating) out of 5 stars")
.accessibilityAdjustableAction { direction in
switch direction {
case .increment:
rating = min(rating + 1, 5)
case .decrement:
rating = max(rating - 1, 1)
@unknown default:
break
}
}
}
}
In Accessibility Inspector, hover over this control and verify:
- Label reads “Rating for Toy Story” (not five separate stars).
- Value reads “3 out of 5 stars”.
- Traits includes Adjustable.
- Actions lists Increment and Decrement.
If any of these are missing or incorrect, the Inspector shows exactly what to fix.
Navigating the Full Hierarchy
Click the Hierarchy button (tree icon) to see the complete accessibility tree for the current screen. This mirrors what VoiceOver navigates. Look for:
- Redundant elements — Children that should be grouped under a single parent.
- Missing elements — Interactive controls that do not appear in the tree.
- Incorrect ordering — Elements that appear in an illogical navigation order.
Warning: SwiftUI’s accessibility tree does not always match the visual layout order. If VoiceOver reads elements in a confusing sequence, use
.accessibilitySortPriority(_:)to reorder them explicitly.
WCAG 2.1 Color Contrast Checking
Accessibility Inspector includes a built-in color contrast analyzer that evaluates foreground/background color combinations against WCAG 2.1 guidelines.
How to Check Contrast
- In the Inspector, switch to the Color Contrast tab.
- Use the color picker eyedroppers to sample the foreground and background colors from the Simulator.
- The tool calculates the contrast ratio and shows pass/fail status against:
- WCAG AA (normal text): 4.5:1 minimum
- WCAG AA (large text, 18pt+ or 14pt+ bold): 3:1 minimum
- WCAG AAA (normal text): 7:1 minimum
- WCAG AAA (large text): 4.5:1 minimum
Practical Contrast Ratios for iOS Apps
Here are contrast ratios for common Pixar-themed color schemes:
| Combination | Ratio | AA | AAA |
|---|---|---|---|
| Black on White | 21:1 | Pass | Pass |
| System Blue (#007AFF) on White | 4.5:1 | Pass | Fail |
| System Orange (#FF9500) on White | 2.5:1 | Fail | Fail |
| Monsters Inc Purple (#6B3FA0) on White | 6.5:1 | Pass | Fail |
| Ratatouille Gold (#C7923E) on Dark Grey (#2C2C2C) | 5.2:1 | Pass | Pass (large) |
Tip: Use Apple’s system colors whenever possible. They adapt to light/dark mode and accessibility settings (Increased Contrast) automatically, which means they pass WCAG criteria without manual tuning.
Dynamic Type and Contrast
Color contrast issues often surface at specific Dynamic Type sizes. A color combination that passes at the default text size might fail when the user increases their preferred content size, because the background area shrinks and adjacent colors bleed closer together. Test at the largest Dynamic Type setting (AX5) by using the Accessibility Inspector’s Settings panel to override the text size without leaving the tool.
Exporting Audit Reports
Accessibility Inspector can generate shareable HTML reports from audit results. This is invaluable for tracking progress, sharing findings with your team, and documenting compliance.
Generating a Report
- Run an audit on the target screen.
- After the audit completes, click File > Export Audit Report (or use the export button in the audit panel).
- Choose a save location. The report is a self-contained HTML file.
The exported report includes:
- Summary statistics — Total issues found, categorized by severity.
- Per-element details — Each issue with the element’s accessibility properties, a screenshot of the affected element, and the suggested fix.
- Timestamp and target information — Which device, OS version, and app version were audited.
Using Reports for Compliance Documentation
For apps in regulated industries (healthcare, finance, education), accessibility compliance is often a legal requirement. Maintaining a history of audit reports demonstrates due diligence:
accessibility-audits/
2026-01-15-v1.0-home-screen.html
2026-01-15-v1.0-movie-detail.html
2026-02-01-v1.1-home-screen.html
2026-02-01-v1.1-movie-detail.html
2026-03-01-v1.2-full-audit.html
Note: Accessibility Inspector’s automated audit covers a subset of WCAG 2.1 criteria. It does not test for motion sensitivity, reading order logic, or semantic correctness of custom rotor actions. Complement automated audits with manual VoiceOver testing and real user feedback.
Integrating Audits into Your CI Pipeline
While Accessibility Inspector is a GUI tool, you can automate parts of the accessibility audit process using XCTest’s accessibility APIs.
Programmatic Accessibility Checks in UI Tests
import XCTest
final class AccessibilityAuditTests: XCTestCase {
let app = XCUIApplication()
override func setUp() {
continueAfterFailure = false
app.launch()
}
@available(iOS 17.0, *)
func testMovieDetailAccessibilityAudit() throws {
// Navigate to the movie detail screen
let movieCell = app.cells["Toy Story 4"]
XCTAssertTrue(movieCell.waitForExistence(timeout: 5))
movieCell.tap()
// Run the built-in accessibility audit
try app.performAccessibilityAudit()
}
@available(iOS 17.0, *)
func testHomeScreenAccessibilityAudit() throws {
// Audit with specific categories
try app.performAccessibilityAudit(for: [
.dynamicType,
.contrast,
.elementDetection,
.hitRegion
])
}
@available(iOS 17.0, *)
func testAllScreensAccessibilityAudit() throws {
let screens = ["Home", "Search", "Favorites", "Profile"]
for screen in screens {
app.tabBars.buttons[screen].tap()
try app.performAccessibilityAudit { issue in
// Filter out known issues you plan to fix later
if issue.auditType == .contrast,
issue.element?.label == "Legacy Banner" {
return false // Skip this known issue
}
return true // Fail on everything else
}
}
}
}
The
performAccessibilityAudit()
method, introduced in iOS 17, runs the same checks as Accessibility Inspector’s audit button but within your test suite.
Failures break the build, preventing accessibility regressions from reaching production.
Apple Docs:
performAccessibilityAudit(for:_:)— XCTest
Filtering Known Issues
The closure parameter lets you acknowledge known issues without silencing the entire audit. This is useful during incremental adoption — you document existing issues and prevent new ones:
try app.performAccessibilityAudit { issue in
// Known: legacy header uses custom font
// that triggers dynamic type warning
if issue.auditType == .dynamicType,
issue.element?.identifier == "LegacyMovieHeader" {
return false // Do not fail for this issue
}
return true
}
Performance Considerations
Audit speed. A full audit on a screen with 50-100 elements takes under 2 seconds on a modern Mac. Screens with
deeply nested List or LazyVStack views may take longer because the Inspector traverses the full accessibility tree,
including offscreen elements that have been materialized.
Simulator vs. device. Auditing on the Simulator is convenient but does not catch device-specific issues like haptic feedback accessibility or hardware keyboard navigation. Run audits on a physical device at least once per release cycle.
performAccessibilityAudit in CI. The XCTest method adds 1-3 seconds per screen to your UI test suite. On a
20-screen app, this is under a minute of additional CI time for a meaningful quality gate. The trade-off is
overwhelmingly worth it.
Dynamic content. Accessibility Inspector audits the current screen state. If your app loads content asynchronously
(movie posters from a network, for example), ensure the content has loaded before running the audit. In XCTest, use
waitForExistence or custom expectations to synchronize.
When to Use (and When Not To)
| Scenario | Recommendation |
|---|---|
| Pre-release quality gate | Run audits on every key screen before each release. Export reports. |
| During active development | Keep Inspector open alongside Simulator. Use hover inspection. |
| CI pipeline regression prevention | Use performAccessibilityAudit() in UI tests to fail on new issues. |
| Color palette design | Use the contrast calculator early, when choosing your color scheme. |
| Full WCAG 2.1 compliance | Inspector covers a subset. Supplement with manual VoiceOver and Switch Control. |
| Testing VoiceOver navigation flow | Inspector shows what VoiceOver sees, but manual testing reveals flow issues. Use both. |
| Debugging a specific VoiceOver bug | Hover inspection pinpoints the exact element and its properties. Faster than Xcode. |
Summary
- Automated audits catch missing labels, insufficient contrast, undersized hit targets, and Dynamic Type issues in a single click. Run them on every screen before shipping.
- Hover inspection shows the live accessibility tree — label, value, traits, actions — for any element. This is your debugging tool for custom accessibility implementations.
- Color contrast checking validates foreground/background combinations against WCAG 2.1 AA and AAA criteria. Test with both light and dark mode, and at the largest Dynamic Type size.
- Exportable HTML reports create an audit trail for compliance documentation and team communication.
performAccessibilityAudit()in XCTest (iOS 17+) brings automated accessibility auditing into your CI pipeline, preventing regressions from shipping.
For the code-level accessibility implementation that pairs with Inspector auditing, see SwiftUI Accessibility. To explore the latest Xcode developer tools including the new diagnostics features, continue to Xcode 26 Features.