pointfreeco/swift-snapshot-testing: snapshot tests for any Swift value, not just UIKit views
📸 Delightful Swift snapshot testing.
At a glance
- What is it?
- The library records a reference of any Swift value, compares later runs against it, and fails when they differ. It is a good fit for view controllers, URL requests, Codable models and plain structs, and a poor fit for teams that cannot pin their simulator or simulator OS.
- Who is it for?
- Adopt it if you already run Swift tests and want image, text or data references checked into the repository, and if you can keep the simulator model and OS fixed for the tests that produce images. Do not adopt it if your suite runs across a shifting matrix of simulators, because the README warns that references must be compared on the same simulator that produced them.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 8 days ago.
- What is it written in?
- Mainly Swift, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What swift-snapshot-testing does that XCTAssertEqual cannot
A normal Swift assertion compares two values you construct in the test. A view controller, a rendered image, a JSON payload or a recursive view hierarchy has no useful equality you can write by hand, and even if you could, the failure message would be a wall of text. SnapshotTesting replaces the hand-written expectation with a file on disk. The first run of an assertion records a reference and fails, printing the path of the newly recorded file. Later runs load that file and compare it with the runtime value, and a mismatch fails the test and describes the difference.
The audience is Swift developers who already have a test target and want regression coverage over output that is expensive to assert directly: UIKit and SwiftUI views, view controllers, URL requests, Codable values and arbitrary structs. The README is explicit that most snapshot libraries in the Swift community stop at UIImage of UIView, while this one accepts any value and any strategy that value supports. That breadth is the reason to pick it over a narrower image-diffing helper.
Snapshot strategies: image, recursiveDescription, raw, json, plist and dump
The mechanism is a value plus a strategy. assertSnapshot takes any value and a snapshot strategy, and the strategy decides how the value becomes bytes. For a view controller you can pass .image for a rendered bitmap and .recursiveDescription for a textual dump of its properties and subview hierarchy, and both can be recorded from the same test.
For non-view values the README lists .raw for URL requests, .json and .plist for Encodable values, and .dump, which uses Swift's Mirror to render any value at all. That last one matters: a struct with no custom conformance still gets a snapshot, which makes it usable as a cheap structural check on model types.
View strategies accept a device, so one simulator can produce references for several form factors. The README shows .image(on: .iPhoneSe), .iPhoneSe(.landscape), .iPhoneX and .iPadMini(.portrait), with the same options available to .recursiveDescription. The trade-off is stated in a warning: snapshots must be compared using the exact same simulator that originally took the reference, otherwise images differ. That single sentence is the constraint that shapes how you run these tests in CI.
Installing swift-snapshot-testing and recording a first reference
There is no configuration step. Once the package is linked, you import SnapshotTesting and call assertSnapshot. The README warns that Xcode will try to add the package to your main application or framework target, and that you should move it to a test target in the final dialog.
For Swift Package Manager, add the package to your dependencies and then to your test target. The README uses from: "1.12.0" as the version requirement.
dependencies: [
.package(
url: "https://github.com/pointfreeco/swift-snapshot-testing",
from: "1.12.0"
),
]Then make the product a dependency of the test target rather than the app target, so snapshot code never ships in the app binary.
targets: [
.target(name: "MyApp"),
.testTarget(
name: "MyAppTests",
dependencies: [
"MyApp",
.product(name: "SnapshotTesting", package: "swift-snapshot-testing"),
]
),
]The first assertion you write records instead of comparing. The README's example uses Swift Testing, with the test annotated @MainActor and the assertion called with .image as the strategy.
import SnapshotTesting
import Testing
@MainActor
struct MyViewControllerTests {
@Test func myViewController() {
let vc = MyViewController()
assertSnapshot(of: vc, as: .image)
}
}Run that test and it fails with "No reference was found on disk. Automatically recorded snapshot", followed by an open command pointing at a path under __Snapshots__/MyViewControllerTests/. Run it a second time and it compares against the file it just wrote. When you intentionally change the view, record again: pass record: .all on the single assertion, or wrap several assertions in withSnapshotTesting(record: .all). For a whole suite, the README shows .snapshots(record: .failed) on a Swift Testing suite and an invokeTest override for XCTestCase subclasses.
The simulator pin is the real cost of image snapshots
Image snapshots are the reason most people arrive, and they are also where the library asks the most of your CI setup. The README's warning is unambiguous: compare snapshots using the exact same simulator that originally took the reference, to avoid discrepancies between images. A reference recorded on iOS 13.3 against an iPhone 11 Pro Max is not portable to a different OS or device without re-recording, and the failure will look like a pixel diff rather than a configuration error.
The repository's own Makefile shows how narrow the tested destinations are. test-ios runs xcodebuild test with -destination platform="iOS Simulator,name=iPhone 11 Pro Max,OS=13.3", and test-tvos uses Apple TV 4K on OS 13.3. There is no matrix of devices. If your project needs references that survive an Xcode upgrade, plan to re-record them as part of that upgrade, and treat the snapshot diff as a signal to inspect rather than an automatic accept.
There is a second boundary: the README does not document a tolerance or precision setting for image comparison, so a test either matches the reference or fails. Teams that want fuzzy image matching with a configurable threshold are looking at a different tool. The related searches include "swift snapshot testing precision", which suggests people expect such a knob; the README as given does not describe one.
Where snapshot testing is the wrong tool
Snapshots are cheap to write and expensive to trust. A reference file that nobody reads still passes, so a snapshot suite can grow into a large set of images and dumps that lock in behaviour nobody intended to freeze. If your team accepts new references without looking at the diff, the tests stop being an assertion and become a change log.
It is also the wrong tool for logic. A snapshot of a Codable value tells you the encoding changed; it does not tell you which branch produced it. Keep ordinary assertions for control flow, and use snapshots for output shape.
Finally, the library is not a UI test runner. It does not drive taps or navigate flows. The README's examples construct a view controller and snapshot it; interaction belongs to XCTest and XCUITest, which are separate concerns the README does not attempt to replace. If your problem is "the app crashes when I tap this button", a snapshot of the screen before and after will not find it.
swift-snapshot-testing compared with image-only snapshot libraries
The closest alternative in the Swift ecosystem is an image-only snapshot library, typically built around UIImage or UIView and a pixel comparison. The difference is scope, not quality. An image-only tool gives you one strategy and one output format, which makes it simpler to reason about and often easier to wire into a screenshot-diff service. SnapshotTesting gives you a strategy protocol and ships image, recursiveDescription, raw, json, plist and dump, so the same assertion style covers a URL request and a model struct as well as a rendered screen.
That breadth has a cost in review discipline. A .dump snapshot of a struct changes whenever any stored property changes, including ones you do not care about, and a .recursiveDescription snapshot of a view hierarchy changes when private view internals shift between OS versions. The image strategy has the simulator pin described above. So the choice is between a narrow tool with fewer moving parts and a broad tool that asks you to pick the right strategy per test. For a codebase that already tests models and networking in Swift, the broad tool removes the need for a second library.
Licence, releases and what maintenance looks like
The repository is MIT licensed, which permits use, modification and redistribution provided the copyright notice and permission notice are included; the LICENSE file sits at the repository root. That is the usual permissive arrangement, and it means snapshot references you generate are your own artifacts, not something the licence governs. This is not legal advice; read LICENSE before redistributing.
The repository is not archived, and the last push was on 2026-09-21. Releases 1.19.6, 1.19.5 and 1.19.4 landed on 2026-09-21, 2026-09-15 and 2026-07-28 respectively, so the cadence over that window is roughly monthly. The README documents no migration steps between 1.x releases, so upgrade cost is mostly re-recording: if a release changes how a strategy renders, references recorded under the old version will diff and need to be regenerated. Budget for that after any version bump that touches the strategies you use, and check the release notes for the specific version rather than assuming the diff is your bug.
Editorial conclusion
Adopt it if you already run Swift tests and want image, text or data references checked into the repository, and if you can keep the simulator model and OS fixed for the tests that produce images. Do not adopt it if your suite runs across a shifting matrix of simulators, because the README warns that references must be compared on the same simulator that produced them. Before writing a single reference, decide which strategies you need, confirm the package is linked to a test target rather than the app target, and check the record setting you want, since assertSnapshot takes record: .all inline and withSnapshotTesting applies it to a scope.
Frequently asked questions
What is snapshot testing in Swift?
It records a reference of a value to disk on the first run and compares later runs against that file, failing when the two differ. In swift-snapshot-testing the value can be a view controller, a URL request, an Encodable model or any struct, depending on the strategy you pass to assertSnapshot.
How does swift-snapshot-testing work with Swift Testing?
The README shows a @MainActor struct with a @Test method that calls assertSnapshot(of: vc, as: .image). For suite-wide recording it shows @Suite(.snapshots(record: .failed)) on a Swift Testing suite.
How do I install swift-snapshot-testing with Swift Package Manager?
Add the package URL with from: "1.12.0" to your dependencies, then add .product(name: "SnapshotTesting", package: "swift-snapshot-testing") to your test target. In Xcode, the README warns to move the package from the app target to a test target in the final dialog.
Why does my swift-snapshot-testing test fail after an Xcode or simulator change?
The README warns that snapshots must be compared using the exact same simulator that originally took the reference, to avoid discrepancies between images. Changing the simulator model or OS version invalidates image references, so they need to be recorded again.
How do I re-record a snapshot in swift-snapshot-testing?
Pass record: .all on a single assertSnapshot call, or wrap several calls in withSnapshotTesting(record: .all). The README also shows .snapshots(record: .failed) for a Swift Testing suite and an invokeTest override for an XCTestCase subclass.
Can swift-snapshot-testing snapshot something other than a UIView?
Yes. The README lists strategies for URL requests (.raw), Encodable values (.json and .plist), view hierarchies (.recursiveDescription) and any value through its Mirror (.dump).
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/pointfreeco-swift-snapshot-testing)