Mac-TreeSpace
Why this exists
A minimal native SwiftUI app that answers one question: what is taking up the space? Expandable size-sorted tree, totals grouped by file extension, and a duplicate finder — all readable end to end and yours to hack on.
Duplicates, cheapest test first
Hashing every file to find duplicates is correct and slow. The staged approach spends almost no I/O before committing to a lot:
- Group by size. Different byte counts cannot be identical. Costs a directory walk, no file reads.
- Hash the first 64 KB. Files that differ early drop out, having cost 64 KB each.
- Full SHA-256. Only the survivors get read end to end.
Cost now scales with how many near-duplicates you have, not how many files. Both stages use CryptoKit, so there is no C interop and no extra dependency.
Measuring honestly
Sizes use allocated bytes — the same figure Finder shows as “Size on disk” — not logical length. A folder of 10,000 tiny files can have a trivial logical total and eat gigabytes of real space.
Two rules that prevent the classic bugs:
- Symlinks are skipped, so a recursive walk can never loop.
- Bundles count as single items. An
.appis technically a directory; nobody wants its innards in a size tree.
The scanner walks once and accumulates on the way back up, so every file is stat’ed exactly once.
Permission reality
Scanning Desktop, Documents, Downloads, or ~/Library triggers a macOS prompt the first time. Reading ~/Library or the whole home folder needs Full Disk Access in System Settings → Privacy & Security.
No API grants that, and no app can ask on your behalf. All it can do is fail clearly and point at the right settings pane.
What it deliberately is not
Not a treemap, not a beautification project. Three views, a per-row bar showing each item’s share of the root, right-click to reveal in Finder or move to trash. The bar is the quietly important part — it turns “what’s eating my disk” into a visual scan rather than mental arithmetic.
TreeSpace
A native SwiftUI Mac app that shows what’s eating your disk — like TreeSize for Windows or Disk Inventory X, but minimal and yours to hack on.
Features
- Size-sorted tree: expandable folder view with per-row bars showing each item’s share of the root.
- By Extension: totals grouped by file type — tap a row to list every matching file sorted by size.
- Find Duplicates: groups files by size, then screens with a 64 KB head-hash, then confirms with full SHA-256. Shows wasted space per group.
- Reveal in Finder / Move to Trash: right-click any row, or use the buttons in the duplicates view.
Files are measured using their allocated size on disk (same number Finder’s Get Info shows for “Size on disk”). Symlinks are skipped to avoid cycles. Bundles like .app are treated as single items.
Running it
You need Xcode (or at least the command-line developer tools) on a Mac running macOS 13 or later.
Option A — Open in Xcode
- Double-click
Package.swift, or in Xcode: File → Open… and selectPackage.swift. - Hit the run button (⌘R).
Option B — Command line
cd TreeSpace
swift run
The first build takes a minute while SwiftPM pulls toolchains.
First-time permission prompt
macOS will prompt the app for access the first time you pick a folder that macOS considers sensitive (Desktop, Documents, Downloads). Click Allow. If you want to scan ~/Library or the whole home folder, you’ll need to grant the app Full Disk Access in System Settings → Privacy & Security → Full Disk Access — add the built binary (or Xcode itself, if running from there).
How it works
Scanner.swift— single-pass recursive directory walk usingFileManager.contentsOfDirectorywith resource keys. Accumulates sizes on the way back up.DuplicateFinder.swift— three-stage filter: same-size → same-first-64KB-hash → same-SHA256. UsesCryptoKit.FileNode.swift— reference-type tree so SwiftUI doesn’t copy it.OutlineGroupusessortedChildrenfor expansion.AppState.swift— scan/dup work runs on a backgroundDispatchQueue, posts throttled progress back to the main actor.
Tweaking it
- Minimum duplicate size:
DuplicateFinder.find(..., minSize:)defaults to 64 KB. Lower it if you want to catch smaller junk. - Hidden files: toggle
.skipsHiddenFilesinScanner.swift. - Bundle handling: remove the
isPackagecheck inScanner.swiftif you want to drill into.appcontents. - Color thresholds:
NodeRow.barColorcontrols when bars turn orange/red.
File layout
TreeSpace/
Package.swift
README.md
Sources/TreeSpace/
TreeSpaceApp.swift # @main entry + menu commands
AppState.swift # ObservableObject, scan/dup orchestration
FileNode.swift # tree model
Scanner.swift # recursive sizer
DuplicateFinder.swift # SHA-256 duplicate detection
ExtensionStats.swift # group-by-extension aggregator
ContentView.swift # split view + toolbar + sidebar
TreeView.swift # outline tree + row + file icon
ExtensionsView.swift # extension totals + file list
DuplicatesView.swift # duplicate groups
Support TreeSpace
If this project is useful to you, you can support its maintenance with a coffee: