# AccessibilitySnapshot: regression testing for the iOS accessibility hierarchy

> AccessibilitySnapshot renders the accessibility tree of a view as an image so that changes to labels, traits and element order show up as a failed test rather than as a quiet regression. It suits iOS teams who already use snapshot testing, and it inherits that model, including its habit of failing on irrelevant visual differences.

**cashapp/AccessibilitySnapshot** — Easy regression testing for iOS accessibility

- Repository: https://github.com/cashapp/AccessibilitySnapshot
- Stars: 750 · Forks: 98
- Language: Swift
- License: Apache-2.0
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/cashapp-accessibilitysnapshot

## The accessibility tree as a reviewable image

AccessibilitySnapshot makes it simple to add regression tests for accessibility in iOS apps, and the mechanism is the one people already know: it builds on the idea of snapshot testing to provide snapshots of the accessibility hierarchy.

The problem it solves is narrow and real. Accessibility regressions are almost never caught by unit tests. A label changes from a noun to a sentence, a view loses its accessibility trait, a stack view reorders so that a button is now announced before its label, and none of that breaks a build. It ships. A VoiceOver user hears something slightly wrong, and nobody notices until a customer files a report.

Turning the accessibility hierarchy into an image solves the review problem. A picture of the tree, with the elements laid out, can be committed to the repository and reviewed in a pull request the same way a UI snapshot is. A change that adds an element, reorders elements or alters a label shows up as a visual diff that a human has to look at and accept.

The topics list names the assistive technologies in scope: voiceover, switch-control and voice-control, alongside uikit, swiftui, a11y and testing. The repository is from CashApp, and the licence file carries Copyright 2020 Square Inc., which is the earlier name of the same company.

## Two engines, one assertion, and what you have to set up first

By default, AccessibilitySnapshot uses SnapshotTesting from Point-Free to record snapshots and perform comparisons, and the framework also includes support for using iOSSnapshotTestCase as the snapshotting engine instead.

The precondition is stated in the readme: before setting up accessibility snapshot tests, make sure your project is set up for standard snapshot testing. AccessibilitySnapshot is a layer on top of an existing snapshot mechanism, not a replacement for one, and that dependency shapes what you get. You inherit the recording mode, the diffing, the file naming and the failure output of whichever engine you pick.

That is a genuine trade-off rather than a free win. The benefit is that your accessibility checks live in the same test suite and the same review flow as your pixel tests, and you learn one assertion style. The cost is that you inherit snapshot testing's sharpest edge, which is that a diff is a diff whether or not it matters. An accessibility snapshot that changes because of a colour, a shadow or a layout shift unrelated to accessibility is still a failing test that someone has to triage.

The Extensions section points at a third-party integration for Playbook, which is described as bringing accessibility snapshot testing support to Playbook, and invites others to add their own.

The library also splits into two products. To use only the core accessibility parser, you add a dependency on the Core library alone, AccessibilitySnapshotCore, rather than the full package. The repository layout confirms the split, with a separate AccessibilitySnapshotModel/ directory next to Sources/.

## Setting it up with Swift Package Manager, including the Core-only variant

The install is a Swift Package Manager dependency in your Package.swift:

```swift
dependencies: [
    .package(name: "AccessibilitySnapshot", url: "https://github.com/cashapp/AccessibilitySnapshot.git", from: "0.4.1"),
]
```

Then the test target needs to depend on it:

```swift
targets: [
    .target(name: "MyApp"),
    .testTarget(name: "MyAppTests", dependencies: ["MyApp", "AccessibilitySnapshot"])
]
```

And if all you want is the parser, the Core library on its own:

```swift
targets: [
    .target(name: "MyApp"),
    .testTarget(name: "MyAppTests", dependencies: ["MyApp", "AccessibilitySnapshotCore"])
]
```

The version floor in that snippet is 0.4.1, and the published releases have moved well past it, with 0.13.1 on 2026-09-17 following 0.13.0 the same day and 0.12.1 on 2026-08-14.

If you would rather use iOSSnapshotTestCase for the image comparison, the dependency is different: FBSnapshotTestCase+Accessibility for Swift testing, or FBSnapshotTestCase+Accessibility-ObjC for Objective-C.

```swift
targets: [
    .target(name: "MyApp"),
    .testTarget(name: "MyAppTests", dependencies: ["MyApp", "FBSnapshotTestCase+Accessibility"])
]
```

Two platform requirements are worth checking before you plan the work: Xcode 13.2.1 or later and iOS 13.0 or later. And one structural requirement that catches people out, stated in the readme: accessibility snapshot tests require that the test target has a host application, because the framework needs a real window and view hierarchy to render.

## The .accessibilityImage strategy, and what it shows you

With SnapshotTesting, the integration is a snapshotting strategy called .accessibilityImage, used with the standard snapshot assertions:

```swift
func testAccessibility() {
    let view = MyView()
    // Configure the view...

    assertSnapshot(matching: view, as: .accessibilityImage)
}
```

The customization point is the activation point indicator. By default, the indicators are shown when the activation point is different from the default activation point for that view, and you can override it per snapshot:

```swift
func testAccessibility() {
    let view = MyView()
    // Configure the view...

    // Show indicators for every element.
    assertSnapshot(matching: view, as: .accessibilityImage(showActivationPoints: .always))

    // Don't show any indicators.
    assertSnapshot(matching: view, as: .accessibilityImage(showActivationPoints: .never))
}
```

This is the setting that changes what a diff means. The activation point is where VoiceOver focuses when it lands on an element, and for a custom control it is frequently somewhere the developer did not expect. Rendering the indicator for every element makes an incorrect activation point impossible to miss, which is exactly the class of bug that a plain text dump of the hierarchy would hide. Rendering no indicators gives you a cleaner image in which only the text and the order change.

The choice is per snapshot rather than global, which is the right granularity: a test about labels and order wants the quiet image, a test about focus behaviour wants the loud one.

With iOSSnapshotTestCase the equivalent call is a function rather than a strategy, and it takes the same activation point options:

```swift
func testAccessibility() {
    let view = MyView()
    // Configure the view...

    // Show indicators for every element.
    SnapshotVerifyAccessibility(view, showActivationPoints: .always)

    // Don't show any indicators.
    SnapshotVerifyAccessibility(view, showActivationPoints: .never)
}
```

## Objective-C support, identifiers, and the same recording workflow

The library is not Swift-only, which matters for teams with an existing Objective-C codebase or mixed targets. The readme shows the Objective-C entry point:

```objc
- (void)testAccessibility;
{
    UIView *view = [UIView new];
    // Configure the view...

    SnapshotVerifyAccessibility(view, @"identifier");
}
```

The identifier argument is the other feature worth understanding, and it is not specific to the accessibility side. Because AccessibilitySnapshot sits on iOSSnapshotTestCase, it uses the same recording mechanism, through the self.recordMode property, and supports the same features, among them device agnostic file names and specifying an identifier for each snapshot. The device agnostic naming matters for CI: a snapshot file whose name does not encode the device or OS version will not churn every time a CI image is updated.

The Swift equivalent takes the identifier as a labelled argument:

```swift
func testAccessibility() {
    let view = MyView()
    // Configure the view...

    SnapshotVerifyAccessibility(view, identifier: "identifier")
}
```

The repository's Example directory shows how a real project wires this up, with a Workspace.swift and a Project.swift, a Tuist directory, and separate folders for SnapshotTests, UnitTests, an AccessibilitySnapshot demo, a previews demo and previews tests. The presence of a previews demo is a hint that SwiftUI previews are a supported surface, which matches the swiftui topic tag.

## What the framework cannot see, and the limits of an image-based check

There are two limits worth understanding before you rely on it.

The first is that the framework renders the accessibility hierarchy of the view you hand it. It is not an audit of the running application. A screen assembled from several views, a container that only becomes visible after a data load, or a control whose accessibility label is computed in a view model rather than on the view, will not be in the snapshot. The readme's own requirement that the test target has a host application exists precisely because a real window is needed to build a real hierarchy, and a snapshot is only as good as the tree it was given.

The second is that the check is visual, so the review of it is visual. A reviewer looking at a diff of two accessibility images has to read the rendered text and compare it, which is easier than reading raw dictionaries and far easier than manually running VoiceOver through a screen, but it is still a human reading a picture. There is no assertion in the README about a specific label, a specific trait or a specific element count, and the readme does not document one. If you want the machine to fail on a missing accessibility label, this is the wrong tool and you need assertions on the accessibility attributes instead.

What you get instead is cheap coverage of many screens, because a single assertSnapshot call can be added to every view in a codebase and the review burden is proportional to the number of changed images rather than to the number of screens a tester has to walk through with VoiceOver turned on.

## Conclusion

AccessibilitySnapshot fits an iOS team that already runs snapshot tests and wants the accessibility hierarchy covered by the same regression mechanism, and it does not fit a team that has no snapshot testing set up, since the readme requires that first. Before adopting it, confirm your test target has a host application, check that the accessibility elements you care about are in the tree the library renders, and decide whether the activation point indicators should be part of the committed image, since they change what a diff means.

## FAQ

### What does AccessibilitySnapshot do and what problem does it solve?

It makes it simple to add regression tests for accessibility in iOS apps, building on snapshot testing to provide snapshots of the accessibility hierarchy. That turns changes to labels, traits or element order into a reviewable image diff rather than a silent regression.

### How do I install AccessibilitySnapshot with Swift Package Manager?

Add the package dependency to your Package.swift with the URL and a version from 0.4.1, then list AccessibilitySnapshot in the dependencies of your test target. If you only need the parser, depend on AccessibilitySnapshotCore instead, and for iOSSnapshotTestCase based comparison use FBSnapshotTestCase+Accessibility.

### What are the requirements for AccessibilitySnapshot?

Xcode 13.2.1 or later and iOS 13.0 or later, and the test target must have a host application. The readme also says to have standard snapshot testing set up before adding accessibility snapshot tests, because the framework builds on an existing snapshotting engine.

### What is the .accessibilityImage snapshotting strategy?

It is the SnapshotTesting strategy that AccessibilitySnapshot provides for use with the standard snapshot assertions, for example assertSnapshot(matching: view, as: .accessibilityImage). It can be customized with showActivationPoints set to .always or .never, controlling whether the activation point indicator is drawn for each element.

### Can I use AccessibilitySnapshot from Objective-C?

Yes. The readme shows the Objective-C call SnapshotVerifyAccessibility with a view and an identifier string, and the repository publishes a separate FBSnapshotTestCase+Accessibility-ObjC dependency for Objective-C projects using iOSSnapshotTestCase.

## Sources

- [cashapp/AccessibilitySnapshot on GitHub](https://github.com/cashapp/AccessibilitySnapshot)
- [Issues](https://github.com/cashapp/AccessibilitySnapshot/issues)
- [License: Apache-2.0](https://github.com/cashapp/AccessibilitySnapshot/blob/main/LICENSE)
- [README](https://github.com/cashapp/AccessibilitySnapshot/blob/main/README.md)
- [Releases](https://github.com/cashapp/AccessibilitySnapshot/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/cashapp-accessibilitysnapshot
