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

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:

  1. 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.

  2. 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.

  3. 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

  1. Launch your app in the Simulator (or on a connected device).
  2. Navigate to the screen you want to audit.
  3. In Accessibility Inspector, select the correct target from the dropdown.
  4. Click the Audit button (the checkmark icon in the toolbar) or press Cmd+Shift+A.
  5. 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:

IssueElementCategory
Element has no descriptionMovie poster ImageMissing Label
Potentially inaccessible textMovie title TextColor Contrast
Potentially inaccessible text”Watch Tonight” ButtonColor Contrast
Hit area may be too smallIndividual star ImageHit 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

  1. Click the Inspection button (crosshair icon) in the toolbar.
  2. Hover over any element in the Simulator.
  3. 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.

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

  1. In the Inspector, switch to the Color Contrast tab.
  2. Use the color picker eyedroppers to sample the foreground and background colors from the Simulator.
  3. 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:

CombinationRatioAAAAA
Black on White21:1PassPass
System Blue (#007AFF) on White4.5:1PassFail
System Orange (#FF9500) on White2.5:1FailFail
Monsters Inc Purple (#6B3FA0) on White6.5:1PassFail
Ratatouille Gold (#C7923E) on Dark Grey (#2C2C2C)5.2:1PassPass (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

  1. Run an audit on the target screen.
  2. After the audit completes, click File > Export Audit Report (or use the export button in the audit panel).
  3. 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)

ScenarioRecommendation
Pre-release quality gateRun audits on every key screen before each release. Export reports.
During active developmentKeep Inspector open alongside Simulator. Use hover inspection.
CI pipeline regression preventionUse performAccessibilityAudit() in UI tests to fail on new issues.
Color palette designUse the contrast calculator early, when choosing your color scheme.
Full WCAG 2.1 complianceInspector covers a subset. Supplement with manual VoiceOver and Switch Control.
Testing VoiceOver navigation flowInspector shows what VoiceOver sees, but manual testing reveals flow issues. Use both.
Debugging a specific VoiceOver bugHover 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.