Jonathan Spooner's Reclaim 100+ GB from Xcode landed in iOS dev circles with a title that doubled as a confession: "The Cleanup Script I Wish I'd Written Years Ago." Every working iOS engineer recognised the shape. You write a five line rm -rf script after the third time Xcode refuses to build because the disk is full, alias it to xcleanup, and pass it around for a decade. This post answers whether a hand-rolled Xcode cleanup script is still enough in 2026, or whether the same problem now calls for a GUI like CleanMyDev with receipts and a Trash window.
What does a typical Xcode cleanup script actually do?
Most personal Xcode cleanup scripts converge on the same six paths. Here is the canonical shape, derived from Spooner's writeup and the Apple Discussions thread on rogue simulators where engineers post their cleanup commands as forum replies.
#!/usr/bin/env bash
# xcleanup, the script every iOS dev rewrites eventually.
set -euo pipefail
echo "Pre-clean disk:"
df -h /
# 1. DerivedData. Xcode rebuilds it from source on next build.
rm -rf ~/Library/Developer/Xcode/DerivedData/*
# 2. Archives. Only safe if you have already uploaded the build.
rm -rf ~/Library/Developer/Xcode/Archives/*
# 3. iOS DeviceSupport. Re-fetched the first time you attach the device again.
rm -rf ~/Library/Developer/Xcode/"iOS DeviceSupport"/*
# 4. XCTestDevices. Recreated on next `xcodebuild test`.
rm -rf ~/Library/Developer/XCTestDevices/*
# 5. CoreSimulator caches. Safe. Devices folder is not touched.
rm -rf ~/Library/Developer/CoreSimulator/Caches/*
# 6. Unavailable simulator runtimes. The official knob.
xcrun simctl delete unavailable
echo "Post-clean disk:"
df -h /
The script is fast, idiomatic, and dangerous the way every long-lived shell script is dangerous. No dry run. No path validation. The Archives line is one typo away from rm -rf ~/Library/Developer/Xcode/, which wipes every project archive on that Mac. The cleanupPeriodDays safety net Anthropic shipped after the Claude Code 472 GB blowup does not exist for Xcode. Apple's xcrun simctl delete unavailable ignores the rogue runtimes in /System/Library/AssetsV2 that the Apple Developer Forums thread 812992 documented at 9 GB on a single Mac.
How does CleanMyDev approach the same cleanup?
CleanMyDev was written by an iOS dev who shipped exactly this kind of script for five years before building the GUI. The Xcode paths it audits are a strict superset of what a personal script touches.
| Category | Path | In a typical Xcode cleanup script | In CleanMyDev |
|---|---|---|---|
| DerivedData | ~/Library/Developer/Xcode/DerivedData/ |
yes, rm -rf |
yes, Move to Trash |
| Archives | ~/Library/Developer/Xcode/Archives/ |
yes, often unconditional | yes, with upload-status warning |
| iOS DeviceSupport | ~/Library/Developer/Xcode/iOS DeviceSupport/ |
yes | yes, per-device row |
| watchOS DeviceSupport | ~/Library/Developer/Xcode/watchOS DeviceSupport/ |
sometimes | yes |
| XCTestDevices | ~/Library/Developer/XCTestDevices/ |
yes | yes |
| CoreSimulator caches | ~/Library/Developer/CoreSimulator/Caches/ |
yes | yes, four subfolders separately |
| CoreSimulator devices | ~/Library/Developer/CoreSimulator/Devices/ |
rarely, risky | yes, opt-in per device |
| Unavailable runtimes | xcrun simctl delete unavailable |
yes | yes, called via API |
Orphaned AssetsV2 runtimes |
/System/Library/AssetsV2/com_apple_MobileAsset_iOSSimulatorRuntime/ |
no | yes, audit-first |
| Module cache | ~/Library/Developer/Xcode/UserData/IB Support/ |
no | yes |
| SPM cache | ~/Library/Caches/org.swift.swiftpm/ |
sometimes | yes |
| SwiftLint cache | ~/Library/Caches/SwiftLint/ |
no | yes |
| Xcode previews | ~/Library/Developer/Xcode/UserData/Previews/ |
no | yes |
| Xcode crash logs | ~/Library/Logs/DiagnosticReports/Xcode_* |
no | yes |
The script does the obvious five. CleanMyDev does the obvious five plus the eight nobody remembers, plus a read-only audit with size and last-modified date before any deletion.
Which one is actually safer?
A Mac cleaner is safe when four properties hold: it shows the path before touching it, deletion is reversible, credentials and source are out of scope, and a typo cannot escalate scope. A typical script passes the third because the author wrote it, and fails the other three. CleanMyDev passes all four by design.
| Safety property | Xcode cleanup script | CleanMyDev |
|---|---|---|
| Shows path and size before deletion | no, prints df only |
yes, per row |
| Reversible (Trash, snapshot, or undo) | no, rm -rf is permanent |
yes, Move to Trash with seven day window |
| Excludes credentials and source | yes, if you wrote it carefully | yes, hardcoded denylist for ~/.ssh, ~/.aws, ~/.gnupg, project .git |
| Dry run mode | rare, requires editing the script | yes, audit-only is the default |
Avoids sudo |
depends | yes, never escalates |
| Per-build CI variant | yes, that is what scripts are for | not the target |
A script is safe in proportion to how recently you read it. A script that has lived in your dotfiles for six years through three macOS upgrades is a loaded foot-gun. The brtkwr "Using Claude to free 200GB" writeup makes the case directly: "Docker was the biggest culprit. The volumes were almost entirely unused, probably from old postgres/redis containers I'd stopped months ago." A pure Xcode script would have missed every byte of that 108 GB. A GUI auditor surfaces it on the first scan.
How much disk does each one actually reclaim?
The Xcode subset is the easy win. The interesting comparison is what the script leaves behind. Numbers below come from the 16-month AI tools disk footprint audit on a 1 TB MacBook Pro after six uncleaned months.
| Category | Reclaimed by Xcode cleanup script | Reclaimed by CleanMyDev (same Mac) |
|---|---|---|
| DerivedData | 31 GB | 31 GB |
| Archives | 12 GB | 12 GB |
| iOS DeviceSupport | 22 GB | 22 GB |
| CoreSimulator caches | 18 GB | 18 GB |
| XCTestDevices | 6 GB | 6 GB |
| Orphaned AssetsV2 runtimes | 0 GB | 14 GB |
| SPM cache | 0 GB | 4 GB |
| Xcode previews | 0 GB | 2 GB |
| Claude Code debug + MCP logs | 0 GB | 47 GB |
| Cursor packfiles | 0 GB | 9 GB |
| Codex sessions and rollouts | 0 GB | 8 GB |
| Docker volumes | 0 GB | 38 GB |
| Ollama and Hugging Face models | 0 GB | 22 GB |
| Total | 89 GB | 233 GB |
A clean script reclaims 89 GB in under a minute. CleanMyDev reclaims 233 GB across the same minute on the Xcode subset plus another 10 minutes of review on the AI and Docker rows. The script wins on speed. CleanMyDev wins on coverage.
What does the CleanMyDev workflow replace, exactly?
The audit-and-trash flow replaces three things: the script, the second-guessing about whether the script took something useful, and the monthly ritual of adding one more du -sh line for the new tool eating disk.
# What CleanMyDev runs under the hood, exposed as a CLI for transparency.
# 1. Audit mode. Read-only. Prints sizes for every known cache path.
cleanmydev audit --json > ~/Desktop/cleanmydev-audit.json
# 2. Review the JSON. Each row has: path, size, last_modified, owning_tool,
# risk_label ("safe" | "rebuilds" | "redownloads" | "credentials" | "danger").
jq '.[] | select(.size_bytes > 1000000000)' ~/Desktop/cleanmydev-audit.json
# 3. Selective trash. Same paths the script uses, but reversible.
cleanmydev clean \
--include xcode.deriveddata \
--include xcode.devicesupport \
--include xcode.archives \
--include xcode.coresim.caches \
--include xcode.coresim.runtimes \
--trash
# 4. Recover from regret. Items live in Trash for seven days by default.
open ~/.Trash
A script does the third step. CleanMyDev does all four. The audit matters when the same Mac runs Claude Code, Cursor, Codex, Ollama, and Docker, because the Xcode bytes are the smallest pile in 2026. The which-AI-coding-tool-uses-most-disk ranking puts Claude Code MCP logs at 47 GB, more than DerivedData plus Archives combined.
Which one should you pick?
Use this decision matrix.
| Your situation | Pick |
|---|---|
| You ship CI builds and need a non-interactive cleanup step | Xcode cleanup script |
| You cannot install third-party apps on a managed Mac | Xcode cleanup script |
| You wrote the script yourself in the last six months and read every line | Xcode cleanup script |
| You want one tool that audits Xcode plus AI plus Docker plus JS caches | CleanMyDev |
You want a Trash window in case you rm -rf the wrong path |
CleanMyDev |
| You want a per-row size and last-modified before any deletion | CleanMyDev |
| Your Mac is on the edge of disk full and you do not have time to debug a script | CleanMyDev |
| You want both | run the script on CI, run CleanMyDev on the laptop |
The script wins on automation. The GUI wins where a typo costs you a release archive. The two are not exclusive and most iOS engineers will keep both.
Is the script enough in 2026?
Five years ago a personal Xcode cleanup script was enough because the Xcode caches were the whole story. The shape of the dev Mac in 2026 is different. The Claude Code 472 GB postmortem is one data point. The AI coding tools disk footprint explainer puts the AI category at 60+ GB on the median working Mac. A script that only knows about ~/Library/Developer/Xcode/ is solving last decade's problem.
Run du -sh ~/.claude ~/.codex ~/.cursor ~/.cache/huggingface ~/.ollama 2>/dev/null once. If those five paths add up to more than DerivedData, the script is no longer the right tool. If they do not, keep the script and move on.
If you want one tool that runs the same five Xcode commands plus the 20 other categories that quietly fill a 2026 Mac, CleanMyDev is $9.99 lifetime at /#pricing. Move to Trash by default, no sudo, no account, no telemetry, no renewal. The audit mode is free, so you can run it next to your script and compare what each one finds before paying. The script stays available if it covers enough. CleanMyDev is there if it does not.