iOS simulators and Xcode
Xcode and the iOS Simulator are often the biggest consumers of disk space on a React Native or iOS developer's Mac, and most of it is invisible: build caches named after hashes, simulators nobody remembers creating, runtimes for iOS versions you stopped testing, and screen recordings buried deep inside simulator containers.
lu-cleaner splits this into two categories:
| Category | What it holds |
|---|---|
xcode |
DerivedData, archives, device support symbols, Xcode caches, CocoaPods, SwiftPM, Carthage, fastlane |
simulators |
simulator devices and runtimes, XCTest recordings, simulator logs and caches, Xcode-managed device sets |
Per-project folders such as ios/Pods and ios/build are project artifacts, covered in React Native projects.
Quick start
Section titled “Quick start”lu-cleaner scan -c xcode,simulators # what takes space; nothing is touchedlu-cleaner clean -c xcode,simulators # picker: nothing selected, press a for the recommended itemslu-cleaner devices # simulators and Android emulators only (press a for smart select)A typical scan looks like this (sizes are illustrative):
iOS Simulators 50.2 GB · 6 items · ★ 35.0 GB recommended NAME SIZE AGE RISK RECO iPhone 16 Pro · iOS 18.5 31.6 GB 3d caution XCTest recordings · iPhone 16 Pro 24.8 GB 3d safe ★ iOS 18.5 runtime (22F77) 8.40 GB now moderate ! used by 3 simulator(s) — deleting it makes them unavailable iOS 17.5 runtime (21F79) 7.90 GB 5mo moderate ★ iPhone 14 · iOS 16.4 (unavailable) 2.30 GB 8mo safe ★ iPad Air · iOS 18.5 1.40 GB 2h info ! booted — shut it down before deleting it
Xcode 19.5 GB · 5 items · ★ 14.0 GB recommended NAME SIZE AGE RISK RECO MyApp (workspace gone) 6.20 GB 5w safe ★ MyApp 4.90 GB 2h safe ★ MyApp 2.4.0 (318) 3.10 GB 4mo caution ! latest archive of this app — keep it (or its dSYMs) to symbolicate crashes iOS 17.6.1 (21G93) 2.90 GB 9mo moderate ★ iOS 18.6 (22G86) 2.40 GB 2w moderate ! newest iOS symbols — kept for on-device debugging
★ marks what smart select picks. Lines starting with ! are warnings: an item with a warning is never recommended. info in the risk column means the item is listed for information and cannot be selected (report-only, or blocked right now, like a booted simulator).
DerivedData
Section titled “DerivedData”Xcode keeps the build products, index and build logs of every workspace in ~/Library/Developer/Xcode/DerivedData/<Project>-<hash>. The hash is derived from the workspace path, so every clone and every git worktree of a project gets its own folder, often several GB each. With AI agents creating a worktree per task, DerivedData grows quickly.
lu-cleaner lists one item per folder and names it after the workspace recorded in the folder's info.plist, so you never have to decode the hash:
MyApp: the build cache of an existing workspace. The item note says which one, for example~/dev/my-app/ios/MyApp.xcworkspaceor~/.codex/worktrees/a1b2/my-app/ios/MyApp.xcworkspace.MyApp (workspace gone): the workspace no longer exists (removed worktree, deleted clone). Nothing will ever reuse this cache.
Both are safe: the next build recreates everything, at the cost of a full rebuild and re-indexing. Because DerivedData is a pure cache, smart select recommends every folder, including the project you are building today. If you want to keep incremental builds, deselect that project after pressing a in the picker, or only target folders you have not built for a while:
lu-cleaner clean --yes -k xcode-derived-data --older-than 14d --dry-runA few details:
- Last use comes from Xcode's own
LastAccessedDate, or from the newest build log, build product or index write, so command-linexcodebuildbuilds count too. - Custom location. If you moved DerivedData in Xcode › Settings › Locations, lu-cleaner reads that setting and scans both locations. Per-workspace (relative) locations cannot be scanned globally.
- Shared module cache.
ModuleCache.noindexandSymbolCache.noindexhold precompiled Clang and Swift modules shared by every project. They are safe; the first build afterwards is slower. - Gone means provably gone. A workspace on an external drive that is not mounted is not treated as deleted: lu-cleaner only adds
(workspace gone)when it can prove the path no longer exists. - DerivedData items are skipped while Xcode or
xcodebuildruns.
Archives
Section titled “Archives”Product › Archive writes an .xcarchive to ~/Library/Developer/Xcode/Archives/<date>/. It holds the shipped binary and its dSYMs, the debug symbols needed to symbolicate crash reports of that exact build. An archive cannot be rebuilt identically: a new build gets new binary UUIDs, and the crash reports of the old build stay unreadable.
That is why archives get the strictest handling in this category:
- Risk caution. Archives are never recommended, and
clean --yesleaves them out unless you pass--risk caution. - Moved to the Trash, not deleted. You can still put one back, but the space is only freed once you empty the Trash.
- Newest archive of each app flagged. The most recent archive per bundle identifier carries the warning latest archive of this app — keep it (or its dSYMs) to symbolicate crashes.
Items are named after the app, its version and build number, for example MyApp 2.4.0 (318), and are skipped while Xcode runs.
Device support
Section titled “Device support”When you run an app on a physical device, Xcode copies the debug symbols of the device's OS version to ~/Library/Developer/Xcode/iOS DeviceSupport/<version> (<build>). That is 2 to 6 GB per OS version, and a new folder appears each time a device you debug on gets an OS update. watchOS, tvOS, visionOS and macOS have their own … DeviceSupport folders.
- The newest version of each platform is kept. It carries the warning newest iOS symbols — kept for on-device debugging and is never recommended.
- Older versions are moderate and recommended once stale (unused for longer than
stale_after, 14 days by default). - Cost: the next time a device running that OS version is used for debugging, Xcode copies the symbols again, which takes several minutes.
Other Xcode data
Section titled “Other Xcode data”These fixed locations come from the catalog, which lists the exact globs.
| Item | Location | Risk | Notes |
|---|---|---|---|
| Xcode app caches | ~/Library/Caches/com.apple.dt.* |
safe | Xcode, xcodebuild and Instruments caches |
| Install products | ~/Library/Developer/Xcode/Products/* |
safe | output of Build For › Installing |
| Documentation cache | ~/Library/Developer/Xcode/DocumentationCache/* and DocumentationIndex/* |
safe | fetched again when the documentation window opens |
| Export staging | $TMPDIR/XcodeDistPipeline.*, $TMPDIR/*.xcdistributionlogs |
safe | only when older than 24 hours |
| Component downloads | ~/Library/Developer/DVTDownloads/* |
moderate | staging area; installed components are not affected |
| Device crash logs | ~/Library/Developer/Xcode/iOS Device Logs (and watchOS) |
caution | devices purge their own crash logs, so these copies are often the only ones left; never recommended |
| Metal toolchain | runs xcodebuild -deleteComponent metalToolchain |
moderate | only needed to compile .metal files; never recommended |
| CocoaPods download cache | ~/Library/Caches/CocoaPods |
safe | same as pod cache clean --all |
| CocoaPods trunk index | ~/.cocoapods/repos/trunk |
moderate | re-fetched lazily; private spec repos are left alone |
| Legacy CocoaPods master repo | ~/.cocoapods/repos/master |
moderate | full git clone of the Specs repo, unused since CocoaPods 1.8 |
| SwiftPM cache | ~/Library/Caches/org.swift.swiftpm |
safe | same as swift package purge-cache |
| Carthage cache | ~/Library/Caches/org.carthage.CarthageKit |
moderate | carthage bootstrap fetches it again (slow) |
| fastlane caches | ~/Library/Caches/tools.fastlane, ~/.fastlane/frameit |
moderate | session cookies in ~/.fastlane/spaceship are not touched |
Extra Xcode versions
Section titled “Extra Xcode versions”Xcode installs other than the selected one (xcode-select -p), found in /Applications and ~/Applications, are listed with their size but never deleted: uninstalling an Xcode is your decision, and a project may still need it. The note suggests xcodes uninstall <version> or moving the app to the Trash. This check is skipped when the Command Line Tools are selected instead of an Xcode, because lu-cleaner cannot tell which Xcode you use.
Simulators
Section titled “Simulators”lu-cleaner reads the simulator state from xcrun simctl list -j and xcrun simctl runtime list -j: names, runtimes, boot state and last use. If xcrun is missing, or only the Command Line Tools are selected, the simulator part of the scan finds nothing.
Simulator devices
Section titled “Simulator devices”Each simulator is one item, for example iPhone 16 Pro · iOS 18.5. It is removed with xcrun simctl delete <udid>, CoreSimulator's own removal, so Xcode's device list stays consistent. The risk depends on what is inside:
| Simulator | Risk | Smart select |
|---|---|---|
| Has apps installed | caution | never |
No apps (or only UI-test runners, *.xctrunner) |
moderate | when shut down and unused for longer than stale_after |
| Never booted (Xcode's default devices) | moderate | never |
| Booted, or in another transient state | as above | cannot be selected |
Why these rules:
- Apps mean data. Deleting a simulator with apps loses their data, logins and settings, and you have to reinstall your dev build. To wipe a simulator but keep the device, run
xcrun simctl erase <udid>yourself. - Never-booted devices weigh almost nothing. Deleting them frees little and removes them from Xcode's run destinations.
- Booted simulators are in use. The Simulator app and the apps you are testing hold their files. lu-cleaner also re-checks the state right before deleting: a simulator booted after the scan is skipped with simulator … is booted now — shut it down first.
Simulators with a custom name (different from their device type) are flagged in the note: they were usually created by a script or an AI agent for QA.
Unavailable simulators
Section titled “Unavailable simulators”Simulators whose runtime was removed, or is not supported by the selected Xcode, can no longer boot. Each one is its own item, for example iPhone 14 · iOS 16.4 (unavailable), cleaned with xcrun simctl delete <udid> for that device only. lu-cleaner never runs xcrun simctl delete unavailable: it would also delete devices that are only unusable right now.
| Unavailable simulator | Risk | Smart select |
|---|---|---|
| Its runtime is provably gone, and it holds no apps | safe | yes |
| It holds apps with their data | caution | never: reinstalling its runtime would bring them back |
| It may come back: its runtime is still installed or on disk (inside another Xcode, for example), another Xcode is selected, or the runtime image is not mounted after a macOS update | caution, with a warning that says why | never |
Right before deleting, lu-cleaner asks simctl again: a device that became available again, changed runtime or was booted since the scan is skipped.
Orphan device folders
Section titled “Orphan device folders”Sometimes a folder in ~/Library/Developer/CoreSimulator/Devices/ is unknown to simctl: its device.plist is missing or invalid, it is marked deleted, or its runtime is no longer installed. Such a folder cannot be booted or listed, and simctl cannot delete it, so lu-cleaner deletes the folder itself.
Because this bypasses simctl, the checks are conservative:
- orphans are only looked for when simctl lists at least one device in that same folder, which proves simctl's answer describes it;
- a well-formed device of an installed runtime that simctl did not list is never called an orphan;
- folders modified in the last 24 hours are ignored, since they may be being created;
- orphans are moderate and recommended once untouched for 7 days. Leftover app data, if any, is named in the note.
XCTest recordings
Section titled “XCTest recordings”On machines that run UI tests, this is often the single biggest item. During XCUITest automation (xcodebuild test, Maestro, agent-device, argent…), the testmanagerd daemon writes screen recordings inside the simulator, under:
~/Library/Developer/CoreSimulator/Devices/<UDID>/data/Containers/Data/InternalDaemon/<uuid>/tmp/Attachments/
Nothing reads or removes them after the run, and Xcode does not show them. Tens of GB on a single simulator is common.
- One safe item per simulator:
XCTest recordings · iPhone 16 Pro. Only containers that belong tocom.apple.testmanagerdare considered. - Only the recordings are deleted. The
Attachmentsfolder, the simulator and its apps stay. - Partial
*.sb-*files left by aborted writes are counted in the note. - Recommended when the simulator is shut down. While it is booted, the item carries a UI test may still be recording and is not recommended. It is skipped while
xcodebuildruns.
The simulator item itself says Its size includes the XCTest recordings listed separately: clean those first. In most cases you want to keep the simulator and drop only the recordings. If you select both, the simulator deletion already covers the recordings, and lu-cleaner only runs the simulator deletion.
To clean only recordings from a script:
lu-cleaner clean --yes -k ios-simulator-attachments --dry-runLogs and caches inside simulators
Section titled “Logs and caches inside simulators”| Item | Kind (-k) |
Risk | What it is |
|---|---|---|---|
| Simulator unified logs | ios-simulator-logs |
safe | os_log archives (tracev3 files) of shut-down simulators; only the files are removed and logging restarts at the next boot |
| Simulator system caches | ios-simulator-caches |
moderate | media analysis, symbolication, maps and suggestions caches of simulators unused for 14+ days; the first boot afterwards is slower |
| CoreSimulator caches | coresimulator-caches |
safe | ~/Library/Developer/CoreSimulator/Caches and Temp; skipped while the Simulator app runs |
| CoreSimulator logs | apple-coresimulator-logs |
safe | host-side logs in ~/Library/Logs/CoreSimulator/* not written for a week |
| Device-set temp copies | apple-simulator-device-set-tmp |
safe | device_set.plist.sb-* leftovers of interrupted writes; the registry itself is never touched |
The unified logs and system caches items re-check that their simulators are still shut down right before cleaning.
Xcode-managed device sets
Section titled “Xcode-managed device sets”Xcode keeps extra simulators outside the default device set:
| Set | Location |
|---|---|
| SwiftUI Previews | ~/Library/Developer/Xcode/UserData/Previews/Simulator Devices |
| Interface Builder | ~/Library/Developer/Xcode/UserData/IB Support/Simulator Devices |
| XCTest clones (parallel testing) | ~/Library/Developer/XCTestDevices |
| Playgrounds | ~/Library/Developer/XCPGDevices |
Each set larger than 1 MB is one safe item, cleaned with xcrun simctl --set <dir> delete all. Xcode recreates these devices the next time a preview, storyboard, parallel test or playground needs them. They are skipped while Xcode runs.
Runtimes
Section titled “Runtimes”A simulator runtime is a disk image of several GB managed by macOS in system storage. lu-cleaner never touches those files directly: it asks CoreSimulator to remove the runtime with xcrun simctl runtime delete <identifier>, and shows the size simctl reports.
| Runtime | Handling |
|---|---|
| Bundled with Xcode | report only: it goes away with that Xcode |
| Used by simulators | warning used by N simulator(s) — deleting it makes them unavailable |
| Newest of its platform | warning newest iOS runtime — needed to run iOS simulators |
Among the keep_latest newest runtimes of its platform (only when you raise keep_latest above 1) |
never recommended |
| Unused, and a newer runtime of the same platform is installed | recommended |
Runtimes are moderate: re-downloading one (around 8 GB) takes a while, from Xcode › Settings › Components or with xcodebuild -downloadPlatform iOS. They are skipped while Xcode runs. If you delete a runtime that simulators still use, they become unavailable (and show up as … (unavailable) items on the next scan); booted ones are shut down by simctl.
What lu-cleaner only reports
Section titled “What lu-cleaner only reports”The safety guard only deletes inside your home folder and your per-user temp directory, and lu-cleaner never runs sudo. Everything else is listed so you know it exists, with instructions to remove it yourself:
| Item | Why it is report-only | What to do |
|---|---|---|
Stale simulator dyld caches in /Library/Developer/CoreSimulator/Caches/dyld/ |
root-owned | caches built for an older macOS build, or for a runtime that is no longer installed, are never used again; the note gives the exact sudo rm -rf '<path>' command |
| Extra Xcode installs | outside your home folder, and uninstalling Xcode is your call | xcodes uninstall <version>, or move the app to the Trash |
| Runtimes bundled with Xcode | part of the Xcode install | removed together with that Xcode |
| Anything on another volume | deleting it frees nothing on the internal disk | shown as on external volume — no internal gain |
Recommended workflow
Section titled “Recommended workflow”-
Quit Xcode and shut down every simulator:
Terminal window xcrun simctl shutdown all -
Review what is there, with every item listed:
Terminal window lu-cleaner scan -c xcode,simulators --all -
Open the picker. Nothing is selected yet: press a to select the recommended items, or pick with space. Open each category with enter, deselect what you want to keep (the DerivedData of your current project, for example), then press d to clean.
Terminal window lu-cleaner clean -c xcode,simulators -
If the free space did not grow as expected, run
lu-cleaner doctor. Archives only free space once the Trash is emptied; see Disk space not freed for other causes.
The picker keys are listed in Keyboard shortcuts.
Scripting
Section titled “Scripting”# The plan smart select would run; nothing is deletedlu-cleaner clean --yes --smart -c xcode,simulators --dry-run
# Only XCTest recordingslu-cleaner clean --yes -k ios-simulator-attachments
# DerivedData not built for a monthlu-cleaner clean --yes -k xcode-derived-data --older-than 30d
# Unused runtimes and unavailable simulators, recommended ones onlylu-cleaner clean --yes --smart -k ios-simulator-runtime,ios-simulators-unavailableKinds you can pass to -k. The provider id apple selects every kind of the Xcode and simulator scanner at once (with clean --yes, add -c or --smart: it spans two categories); catalog kinds (apple-*) are matched by their own id.
| Kind | Item |
|---|---|
xcode-derived-data |
DerivedData of one workspace |
xcode-module-cache, apple-xcode-module-cache |
shared module cache (custom and default location) |
xcode-archive |
one .xcarchive |
xcode-device-support |
debug symbols of one device OS version |
xcode-metal-toolchain |
the Metal toolchain component |
xcode-app |
extra Xcode install (report only) |
ios-simulator |
one simulator device |
ios-simulators-unavailable |
one unavailable simulator |
ios-simulator-orphan |
a device folder unknown to simctl |
ios-simulator-attachments |
XCTest recordings of one simulator |
ios-simulator-logs, ios-simulator-caches |
unified logs, system caches inside simulators |
ios-simulator-device-set |
a Previews, Interface Builder, XCTest or Playground device set |
ios-simulator-runtime |
one simulator runtime |
ios-simulator-dyld-cache |
stale dyld cache (report only) |
coresimulator-caches |
per-user CoreSimulator caches |
Catalog entries (apple-*) are listed in the catalog. The --json output of scan exposes the same kind field plus per-item metadata such as udid, runtime, workspace or orphan; see JSON output.