My Mac kept saying "Storage almost full", and I wanted to know why. While I was at it, I wanted answers to a few
other questions macOS never really answers: which apps used my camera last week? What starts by itself when I log
in? Which app is quietly draining my battery?
So I built Sweep, a native macOS app that cleans, protects and explains your Mac. It's free and open source
(GPL-3.0), and this post is about how it works under the hood.
amar97march
/
sweep
Clean, protect and understand your Mac. Free and open source
Clean, protect and understand your Mac. Free and open source.
Install · Features · Safe by design · Build from source · Contribute · Support
Sweep finds the junk, big files and forgotten leftovers filling your Mac, checks it for malware and risky settings, keeps every app up to date, and shows you what your Mac does while you're not looking. It's a native SwiftUI app, it runs entirely on your Mac, and nothing is ever removed until you've reviewed it.
One checkup for your whole Mac
Press Scan once: Junk Files, Security, Maintenance, Apps and (if you like) Declutter run one after another, and each card lights up with what it found. Open any card to review it, or run everything at once.
Features
Junk Files: gigabytes back, safely
- App and system caches, logs and crash reports, the Trash, old iPhone backups and downloaded installers.
- Developer junk: Xcode…
What it does
- Junk Files: app and system caches, logs, Xcode DerivedData and simulators, package manager caches, old iPhone backups, local AI models (Ollama, LM Studio, Hugging Face) and Docker's build cache and unused images.
- Disk Map: a live bubble map of any folder, plus What Grew since your last scan.
- Activity: camera and microphone history per app, everything that starts by itself (flagged when something new appears), battery drain by app, network use by app, app usage time, and apps that can see what you type.
- Security: a malware scan using Apple's own XProtect rules, risky startup items, and a quarantine you can restore from.
- Health: a 0–100 score with reasons, battery care, SSD health and a security checklist.
- Updates: every app update from developers, the App Store and Homebrew, in one list.
There's no account and no analytics, and nothing is ever removed until you've reviewed it.
The rest of this post covers the parts I enjoyed building most.
1. Scanning a disk 3× faster than du
The obvious way to measure a folder is to list it, then call stat on every file. On a big tree, that's hundreds of
thousands of system calls.
macOS has a better API: getattrlistbulk(2).
You open a directory, say which attributes you want, and each call fills a buffer with packed records for many
entries at once: name, type, flags, inode number, link count and allocated size.
This is the core loop of Sweep's directory reader, slightly simplified:
let fd = open(path, O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC)
guard fd >= 0 else { return .open(errno: errno) }
defer { close(fd) }
while true {
// One call fills a 256 KiB buffer with records for a few thousand entries.
let count = getattrlistbulk(fd, &attributes, buffer, bufferSize, options)
if count == 0 { return nil } // finished
if count < 0 { return .read(errno: errno) }
var record = UnsafeRawPointer(buffer)
for _ in 0..<count {
let length = record.loadUnaligned(as: UInt32.self)
body(parse(record)) // decode one packed record
record += Int(length)
}
}
The records are tightly packed. Attributes come in a fixed order, each aligned to 4 bytes, and only if they appear
in the "returned attributes" set at the start of the record. Names are stored as an offset and a length
(attrreference_t). Getting parse right took careful reading of getattrlist(2), and a lot of unit tests.
On top of the reader sits a fixed pool of threads (twice the number of cores, at most 32) sharing one LIFO queue of
folders. A LIFO queue makes the walk roughly depth-first, so the list of folders waiting to be read stays small.
Measured on an M-series Mac with a warm cache, with totals that match du byte for byte:
| Folder | Entries | du -sk |
Sweep |
|---|---|---|---|
/Applications |
575 k | 4.6 s | 1.4 s |
/opt/homebrew |
446 k | 3.3 s | 1.0 s |
/Library |
127 k | 0.8 s | 0.5 s |
2. Sizes that are actually right
Fast doesn't matter if the numbers are wrong, so the scanner follows a few strict rules:
-
Bytes allocated on disk, not file length, the same as
du. - Hard links counted once, using the inode number and link count.
-
Never crossing into other volumes. When scanning
/, it also skips/System/Volumes/Data, so firmlinked data isn't counted twice. -
Never following symlinks.
O_NOFOLLOWon every open, and symlinks are reported as entries of their own. - Never downloading iCloud files. Touching an iCloud Drive or File Provider placeholder the wrong way can make macOS download it, which a disk scanner must never do. So every scanner thread opts out first:
setiopolicy_np(IOPOL_TYPE_VFS_MATERIALIZE_DATALESS_FILES,
IOPOL_SCOPE_THREAD,
IOPOL_MATERIALIZE_DATALESS_FILES_OFF)
The command-line tool also has a --private-size option that reports what deleting a file would really free when
APFS clones share its blocks.
3. A cleaner that's careful about deleting
The worst bug a cleaner can have is deleting the wrong thing. So every removal in Sweep goes through one
FileRemover, with a RemovalPolicy that refuses anything risky: paths that aren't plain absolute paths, system
folders like /System/ and /usr/bin/, mount points, and folders that must exist (it removes their contents instead).
The tests use the same remover, locked to a sandbox folder:
/// The standard rules, plus: nothing outside `roots` may be touched.
public static func confined(to roots: [String]) -> RemovalPolicy {
var policy = standard
policy.allowedRoots = roots
return policy
}
All of the nearly 300 tests build their files inside the build folder, use a stand-in Trash, and keep settings in
memory. A test run can't delete, move or change anything outside the project: not your files, not the real Trash.
In the app, every scan ends in a review list you can check, change or cancel, removals are logged, and the Trash is
the default for anything you might want back.
4. Reading what macOS already knows
Most of the "explain your Mac" features don't guess. They read data macOS already keeps:
-
Malware scan: Sweep runs Apple's own XProtect YARA rules in place, plus any
.yarrules you add, with its own YARA engine. - Camera and microphone history: read from the log behind the privacy indicator (the green and orange dots in the menu bar), with 90 days of history.
- Drive health: the SSD's own NVMe health log (life left, data written, temperature, errors), read through the read-only SMART interface, with no administrator rights needed. A small C helper handles this, because Swift can't call that interface directly.
- Battery charge limit: read from the power management policy macOS publishes.
- Intel-only apps: found by reading each app's Mach-O header, so you're warned before Rosetta goes away.
- Updates: each app's own update feed, with the EdDSA signature and the developer team checked, plus the App Store and Homebrew.
5. A SwiftUI interface that moves, but not too much
The whole app is SwiftUI in Swift 6 language mode, so the compiler checks concurrency for data races. The window is
built from a small set of components: a drifting mesh-gradient background with a starfield (macOS 15, with a softer
fallback on macOS 14), glass cards, a comet-style progress ring, and cards that tilt toward the pointer. Disk Map lays
out its bubbles with circle packing.
Two rules kept it from getting out of hand:
- Every continuous animation stops when Reduce Motion is on.
- Every screen is checked automatically. A screenshot harness renders every screen with sample data at three window sizes, and layout tests check that long names wrap or shorten instead of overlapping, from the minimum 1040×680 window up.
Try it
- Download the DMG from Releases (macOS 14 or later). It isn't notarized yet, so the first time you open it, go to System Settings → Privacy & Security and click Open Anyway. The full source is public if you'd like to read what it does first.
- Build it yourself with Xcode 16 or later:
git clone https://github.com/amar97march/sweep.git
cd sweep
swift test
scripts/make-app.sh && open .build/Sweep.app
Help wanted
Sweep is a one-person project so far, and contributions are very welcome. A few good places to start:
- Live bubbles in Disk Map while a scan is still running
- Translations
- The caches of a developer tool you use
- A VoiceOver pass over every screen
- A Homebrew tap, so
brew installworks
If Sweep is useful to you, a ⭐ on GitHub really helps other people find it,
and you can buy me a coffee to support the work.
What's the biggest surprise hiding in your Mac's storage? And what would you want an app like this to explain
next? I'd love to hear in the comments.


















