Authors native mobile accessibility tests covering iOS (Accessibility Inspector, XCUITest `performAccessibilityAudit()` introduced in iOS 17, VoiceOver label/trait/hint verification) and Android (Espresso `AccessibilityChecks.enable()`, Accessibility Scanner, TalkBack traversal, `contentDescription` labelling) with WCAG-aligned checks for element labels, 44pt/48dp touch targets, contrast ratios, and focus order. Use when an iOS or Android app needs automated and manual accessibility test coverage beyond what `xcuitest-suite` or `espresso-suite` provide.
75
94%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Extends the core performAccessibilityAudit() example in
../SKILL.md. The audit is available from iOS 17 and fails the test
automatically when it finds an issue, so no explicit assertion is needed
(wwdc23).
try app.performAccessibilityAudit(for: [.dynamicType, .contrast])Pass an XCUIAccessibilityAuditType option set. Documented audit types include
.dynamicType and .contrast; passing no argument runs all available checks
(wwdc23).
try app.performAccessibilityAudit(for: [.contrast]) { issue in
// Ignore the decorative watermark label (no contrast fix planned)
if let element = issue.element,
element.label == "WatermarkLabel",
issue.auditType == .contrast {
return true // suppress this issue
}
return false
}The closure receives an XCUIAccessibilityAuditIssue; return true to suppress.
Narrow suppressions by both auditType and element.label so real regressions
still fail (wwdc23).
Each call inspects only the currently visible elements. Navigate to every distinct screen and re-run the audit:
func testCheckoutFlowAudit() throws {
let app = XCUIApplication()
app.launch()
try app.performAccessibilityAudit() // Screen 1
app.buttons["place-order-button"].tap()
try app.performAccessibilityAudit() // Screen 2
}accessibilityLabel is the localized string VoiceOver reads to identify an
element, accessibilityHint describes the action result, and
accessibilityTraits communicates purpose - common values are .button, .link,
.header, .image, .staticText, .adjustable (uia). Production code
sets them; XCUITest verifies via XCUIElement.label:
// Production (UIKit)
let submitButton = UIButton()
submitButton.accessibilityLabel = "Submit order"
submitButton.accessibilityHint = "Places your order and charges the saved card"
submitButton.accessibilityTraits = [.button]// Test
func testSubmitButtonLabel() {
let btn = XCUIApplication().buttons["Submit order"]
XCTAssertTrue(btn.exists, "VoiceOver cannot find the Submit button")
XCTAssertEqual(btn.label, "Submit order")
}The minimum control size on iOS and iPadOS is 44x44 pt (hig). Assert via
XCUIElement.frame:
func testSubmitButtonTouchTarget() {
let frame = XCUIApplication().buttons["Submit order"].frame
XCTAssertGreaterThanOrEqual(frame.width, 44, "Touch target width below 44pt")
XCTAssertGreaterThanOrEqual(frame.height, 44, "Touch target height below 44pt")
}Open it from Xcode menu > Open Developer Tool > Accessibility Inspector. It runs
the same checks as performAccessibilityAudit() interactively on real devices and
simulators - use it to diagnose an audit failure before writing a suppression
(wwdc23).
performAccessibilityAudit(), XCUIAccessibilityAuditType, per-screen guidance, continueAfterFailure = true recommendation, suppression closure.accessibilityLabel, accessibilityHint, accessibilityTraits.