Accessible Elements Guide ☑️
I've outlined some of the most relevant best practices related to iOS Accessibility for codebases with UI tests in this answer. Note that even though this answer is geared towards UIKit apps, the same best practices are framework agnostic between SwiftUI and UIKit (only with different API names).
Apply the same reasoning to SwiftUI accessibility View Modifiers to ensure a great User Experience for both types of apps.
I highly recommend paying attention to accessibility in apps because Apple promotes apps that enforce high accessibility standards, and supporting accessibility is more "ethical" software development by service to a wider audience.
Use .accessibilityIdentifier
I am writing this answer to advise others / OP/comments against using .accessibilityLabel
interchangeably with .accessibilityIdentifier
for the sole purpose of enabling UI Testing.
Use . accessibilityIdentifier
rather than .accessibilityLabel
because otherwise, we create a poor User Experience for VoiceOver users:
button.accessibilityLabel = "test" // ❌
reads to the user as "Button. test" which doesn't help the user navigate the screen if they are visually impaired!
button.accessibilityIdentifier = "test" //
Using the identifier means VoiceOver reads the Button's title label text rather than "test".
NB: If you are still unable to find the element, double-check you haven't overridden the .accessibilityIdentifier
configuration in code in a storyboard or xib file. Also check the button is an accessibility element in both places.
Enabling UI testing for buttons within container views
You may be unable to locate a button in UI tests because it's a subview of an accessible element. In order to enable accessibility on the button for UI tests and VoiceOver for the container, use .accessibilityElements
instead:
containerView = UIView()
containerView.isAccessibilityElement = false
containerView.accessibilityElements = [firstLabel, secondLabel, button]
// These elements are subviews of containerView
Setting .accessibilityElements
makes the parent view an Accessibility Container! The advantage here is that we get UI tests as well as an accessible VoiceOver User Experience that allows users to select/navigate within subviews too.
Screen readers go through the elements on a page/screen in the order in which they appear. Set the order you want VoiceOver to read the elements within the .accessibilityElements
property.
.isHittable
in UI Tests
We can now find the button with a simple XCUIElementQuery
subscript:
XCTAssertTrue(app.buttons["test"].isHittable)
I recommend using .isHittable
as outlined in this answer, rather than .exists
because it provides a more robust test. We can see why from the docs:
isHittable
returns true if the element exists and can be clicked,
tapped, or pressed at its current location. It returns false if the
element does not exist, is offscreen, or is covered by another
element.