Cartograph: inspect Swift dependencies and explain reachability

Hi everyone,

I've been working on Cartograph, an open-source tool for exploring dependencies in Swift projects.

When reviewing code, a list of declarations that look unused is only a starting point. I also want to know what references a declaration and why something is being kept before deciding what to change. That's the problem I wanted Cartograph to help with: it reads the compiler's index store and makes those relationships available as a graph you can query.

Running it on its own codebase turned up two type-level dependency cycles while the module-level check was passing. Swift already rejects circular module imports, so that check couldn't catch dependencies cycling between types within a module. I broke the cycles by moving shared definitions into helper types and added type-level checks to CI.

Cartograph is MIT-licensed. Version 0.10.1 is available as a prebuilt macOS universal binary (arm64 and x86_64), requiring macOS 14 or later and a Swift toolchain:

brew install ictechgy/tap/cartograph

From the root of your Swift package, build the package to generate its index, then try these commands. Replace MyType with a declaration in your project:

swift build
cartograph query MyType
cartograph dead --explain MyType
cartograph cycles --level type

query returns JSON; dead --explain prints the retention reason or a path from a retained root. Queries reuse the index without rebuilding. After source changes, rebuild the affected targets to update it. Xcode projects are supported too; setup is in the README.

One default to watch for with libraries: --retain-public is off. If your callers are outside the analyzed project, enable it to preserve public API. An unreachable result only means the declaration wasn't reached from the configured roots in the analyzed graph; it isn't a recommendation to delete it.

Query responses flag observed limitations, including source files newer than their index data and Objective-C sources outside the Swift graph. A fresh index still doesn't prove that every build configuration was covered.

If a result looks wrong on your project, a small reproducer and your Swift/Xcode version would help me investigate.

3 Likes