swift-custom-dump: readable Swift value dumps, textual diffs and equality assertions
A collection of tools for debugging, diffing, and testing your application's data structures.
At a glance
- What is it?
- swift-custom-dump is an MIT licensed Swift package from Point-Free that replaces the standard library's dump with a readable one, adds a textual diff between two values, and provides expectNoDifference and expectDifference assertions for XCTest and Swift Testing. It suits a Swift team whose debug output and test failures are hard to read, and it is a drop-in dependency rather than a framework.
- Who is it for?
- swift-custom-dump fits a Swift team whose test failures and debug output are currently unreadable, because the fix is a dependency and a change to how you assert equality, and the output format mimics Swift syntax closely enough that a diff of two dumps reads like a diff of two source files.
- 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 57 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 October 24, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Why the standard dump becomes unreadable
The motivation section of the README makes a careful argument rather than a vague one, and it is worth following because it explains the design.
Swift comes with a tool for dumping the contents of any value to a string, and it is called `dump`. It prints all the fields and sub-fields of a value into a tree-like description. The README calls it a wonderful tool that can be great for building debug tools that visualize the data held in runtime values, but says sometimes its output is not ideal.
The first case is dictionaries. Dumping a dictionary leads to a verbose output that is hard to read, and the keys are unordered, so the same dictionary can print differently on two runs. The second case is enums, which have a very verbose output. The third is deep nesting, where the tree notation compounds. And there are cases where dump simply does not print useful information, such as enums imported from Objective-C, where the output is just the mangled Objective-C type name.
So the criticism is specific rather than a general dismissal. dump is not wrong, it is a generic reflection dump, and the library's claim is that it is too crude a tool for the cases where output is what you actually care about.
That framing matters for the adoption decision, because a project that only occasionally prints a struct gains little, while a project whose failing test messages are the main way its developers read state gains a lot. The answer to the question what should I use instead is not a competing dump, it is a better rendering of the same information.
customDump mimics Swift syntax, and sorts dictionary keys
The `customDump` function emulates the behavior of dump but provides a more refined output of nested structures, optimizing for readability. Structs are dumped in a format that more closely mimics the struct syntax in Swift, and arrays are dumped with the indices of each element.
import CustomDump
customDump(user)which produces:
User(
favoriteNumbers: [
[0]: 42,
[1]: 1729
],
id: 2,
name: "Blob"
)That is a meaningful change. The output reads like the value's own source syntax, and because array indices are shown, a diff of two dumps can point at which element changed rather than just showing that something changed.
Dictionaries get their own treatment: a more compact format that mimics Swift's syntax, and the keys are automatically ordered. The ordering is the part that is not cosmetic. A dump whose ordering depends on hash iteration order is not reproducible, so a test that compares two dumps cannot compare them as strings, and a diff computed over two such dumps would be full of noise. Sorting the keys is what makes the text diffing in the next section work.
Enums follow the same idea, dumping in a more compact, readable format, so Result.success(42) is printed as Result.success(42) rather than as a tree with a case name and an associated value on separate lines.
The deeply nested case is where the payoff is clearest. A dictionary of enum-of-struct becomes a compact literal rather than a four-level tree, and the same information is now legible at a glance.
diff works because the dump output is stable and textual
The `diff` function is built on top of the dump output, and the README describes it as a very lightweight way to textually diff any two values in Swift.
var other = user
other.favoriteNumbers[1] = 91
print(diff(user, other)!)The output is a unified diff with the changed element marked by a minus and a plus line, and the surrounding structure shown unchanged.
The point of the design is what it does not do. It does not compare the two values for equality and report a boolean, and it does not walk the structures to find a path to the difference. It takes the two dumps as text and diffs them. That is only sensible because the previous section's properties hold: the output is stable between runs, since dictionary keys are sorted, and the output is structured, so a line-oriented diff lines up meaningfully when only one element changed.
The README is candid that extra work is done to minimize the size of the diff when parts of the structure have not changed, such as a single element changing in a large collection. The example appends one user to a list of five and the diff reports the unchanged prefix compactly, with a marker for the four unchanged entries, followed by the added element in full. The Landmarks example, taken from Apple's SwiftUI tutorial application, shows the same idea in a realistic case: favouriting a landmark produces a diff of the before and after state with a single changed boolean, and the rest of the entries collapsed.
That last example is the one to keep in mind. The output is not a debugging aid bolted onto a test framework, it is a way of making a state transition reviewable, and the collapsed unchanged regions are what make a large collection diff readable rather than a wall of text.
expectNoDifference, and why plain equality assertions fall short
The testing half of the package builds on the diff.
XCTest's `XCTAssertEqual` and Swift Testing's `#expect(_ == _)` both allow you to assert that two values are equal, and if they are not the test suite fails with a message. The README shows what that message looks like: the two values printed as their `CustomStringConvertible` descriptions, on one line, which for a struct with a nested array is already hard to read, and for a large collection is unreadable.
The package's answer is a pair of assertions, `expectNoDifference` and `expectDifference`. The first asserts that two values are the same and, on failure, shows the diff rather than the two flattened descriptions. The second, by implication, asserts that two values differ, which is the assertion you want when a test is checking that something changed.
The README's side-by-side comparison of the two approaches, with screenshots of the failure output, is the argument for the whole library in one image: the same failure, reported as a readable diff with the changed line marked, instead of two descriptions in a single line of text.
There is a design point here that is easy to miss. The assertion is not asking the framework to render the value; the rendering is done by customDump and diff, and the framework's equality check is what decides pass or fail. So the diff output is produced by the same code path as the debug output, which means what you see in the console is what you see in the test failure. That consistency is the actual benefit over trying to build a custom failure message on top of a standard assertion.
The `expectDifference` function is the less obvious of the pair and the more interesting, because asserting inequality is a pattern that appears in reducer and state-machine tests, where a test says that performing an action changed the state, and the useful artefact is the diff of the change.
Three customisation protocols for when the default rendering is wrong
The README's Customization section is the part to read before committing, because the default output is not always what you want and there are three protocols for changing it.
`CustomDumpStringConvertible` covers the common case. If your type already has a CustomStringConvertible conformance, the custom dump can use that description instead of reflecting over the fields, which is what you want for a type whose textual form is already meaningful and whose stored properties are not the interesting part.
`CustomDumpReflectable` is for the case where you want to control the reflection itself, that is, which children are dumped and how they are named, while still letting the library do the traversal.
`CustomDumpRepresentable` is the last resort, where the type provides its own representation of how it should be dumped.
The three together are a sensible ladder: try the string conversion first because it is the least work, move to reflection control if the children are wrong, and fall back to a full custom representation if the type genuinely has no field-shaped structure worth showing. That is a well-judged API shape, because each step gives you more control at the cost of more code.
The repository layout is a standard Swift package. There is Package.swift and a second manifest, [email protected], which is the standard Swift Package Manager mechanism for a package that supports a different manifest format on older toolchains. There are Sources/ and Tests/ directories, a CustomDump.xcworkspace used by the Xcode test targets, a Package.resolved file pinning the dependency versions, a .spi.yml for Swift Package Index, a Makefile, and an .editorconfig.
The Makefile is where the testing matrix lives, and it is thorough. The test-all target chains test-linux, test-swift and test-platforms. The Linux target runs the suite inside a swift:$(SWIFT_VERSION) Docker image with the working directory mounted, so the Linux build is tested in a container rather than assumed. The swift target runs swift test twice, once normally and once with --configuration release. The platforms target drives xcodebuild against the workspace for iOS Simulator, macOS, Mac Catalyst and tvOS Simulator destinations, each in both Debug and Release.
A fast release cadence under a permissive licence
On maintenance, the repository is not archived and the last push was on 2026-08-28, on the main branch.
The release history is unusually brisk. The three most recent tags are 1.7.3 from 2026-08-28, 1.7.2 from 2026-08-27 and 1.7.1 from 2026-08-26. Three patch versions in three days, and a patch number rather than a minor one for a change of that cadence suggests these are mostly fixes rather than a stream of new API.
The project is from Point-Free, which is worth noting for two reasons. The first is that the SwiftUI tutorial application they modified for the diff example is the canonical Apple sample, and using it as the worked example is a deliberate choice to show the library against code every Swift developer has already read. The second is that the package sits alongside a family of other Point-Free libraries, and the README has an Other Libraries section pointing at them.
That last point bears on the alternative question, because the real alternative to this library is not a different diffing tool. It is the standard library plus your own effort. Swift's dump exists, XCTest's equality assertion exists, and the difference between them and this library is entirely in how the output is rendered. If you do not have a problem reading your current test failures, this library does not solve a problem you have.
If you do, the cost of adoption is low: add the dependency, swap the assertion, and read the diff. The risk is that the diff is textual, so a change in the dump format between versions changes your test output, and any golden file testing you layer on top of dump output would be sensitive to it. Pin the version if you do that.
The licence is MIT, with a LICENSE file at the root of the tree, and the package is distributed through Swift Package Manager, with the Swift Package Index documentation linked from the README header. For a testing and debugging dependency that sits in a test target rather than in shipped code, MIT is about as permissive as it gets, and this review does not interpret the terms.
One gap to name: the README does not document a minimum Swift version or a minimum platform version for consumers, though the Makefile pins SWIFT_VERSION to 6.2 for the build and the repository carries a second manifest for Swift 6.1. The package manifest is where the real requirement lives, and it is worth reading before you pin a version.
Editorial conclusion
swift-custom-dump fits a Swift team whose test failures and debug output are currently unreadable, because the fix is a dependency and a change to how you assert equality, and the output format mimics Swift syntax closely enough that a diff of two dumps reads like a diff of two source files. It is the wrong choice if you only want prettier debug printing, since the standard dump still works and this is a bigger change, and it is the wrong choice for a project that cannot take a third party dependency in its test target. Before you adopt it, read the Customization section on CustomDumpStringConvertible, CustomDumpReflectable and CustomDumpRepresentable, because the default output is not always what you want, and check the version you pin, since the tags move quickly.
Frequently asked questions
What does swift-custom-dump replace in Swift?
The `customDump` function emulates the behavior of the standard library's dump but provides a more refined output of nested structures, with structs dumped in a format that mimics Swift struct syntax, arrays dumped with element indices, and dictionary keys automatically ordered.
How does diff in swift-custom-dump work?
It uses the output of customDump to build a very lightweight way to textually diff any two values in Swift. The README notes that extra work is done to minimize the size of the diff when parts of the structure have not changed, such as a single element changing in a large collection.
What is the difference between expectNoDifference and expectDifference?
XCTest's XCTAssertEqual and Swift Testing's #expect(_ == _) fail with a message that prints both values as flattened descriptions. The package's expectNoDifference and expectDifference produce a readable diff of the two values instead, using the same rendering path as customDump.
How do I customise how my own types are dumped?
The README's Customization section lists three protocols: CustomDumpStringConvertible for types with a useful textual description, CustomDumpReflectable to control which children are reflected and how they are named, and CustomDumpRepresentable to provide a fully custom representation.
Is swift-custom-dump still being updated?
The repository is not archived and the last push was on 2026-08-28, with the three most recent tags 1.7.3 from 2026-08-28, 1.7.2 from 2026-08-27 and 1.7.1 from 2026-08-26. The licence is MIT.
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-custom-dump)Was this useful?