<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: pyrx.tech</title>
    <description>The latest articles on Forem by pyrx.tech (@pyrx_tech).</description>
    <link>https://forem.com/pyrx_tech</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3940023%2F6528ad54-c509-45d4-8253-ede855c4999c.png</url>
      <title>Forem: pyrx.tech</title>
      <link>https://forem.com/pyrx_tech</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/pyrx_tech"/>
    <language>en</language>
    <item>
      <title>I built a 12 MB native Mac app with zero third-party dependencies. Here's what I learned.</title>
      <dc:creator>pyrx.tech</dc:creator>
      <pubDate>Fri, 22 May 2026 04:54:26 +0000</pubDate>
      <link>https://forem.com/pyrx_tech/i-built-a-12-mb-native-mac-app-with-zero-third-party-dependencies-heres-what-i-learned-52k7</link>
      <guid>https://forem.com/pyrx_tech/i-built-a-12-mb-native-mac-app-with-zero-third-party-dependencies-heres-what-i-learned-52k7</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvqtblvgvuuxgepxkdv87.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvqtblvgvuuxgepxkdv87.png" alt=" " width="800" height="486"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I spent the last several months building a macOS utility app in SwiftUI. The final binary is 12 MB with &lt;code&gt;dependencies: []&lt;/code&gt; in Package.swift — literally zero third-party packages.&lt;/p&gt;

&lt;p&gt;This isn't a brag post. It's a collection of things that surprised me, tripped me up, or made me rethink how I approach macOS development. If you're building (or thinking about building) a native Mac app, hopefully some of this saves you time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why zero dependencies?
&lt;/h2&gt;

&lt;p&gt;It wasn't an ideology thing at first. I just started building and never hit a point where I &lt;em&gt;needed&lt;/em&gt; an external package badly enough to add one.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Networking?&lt;/strong&gt; &lt;code&gt;URLSession&lt;/code&gt; with &lt;code&gt;async/await&lt;/code&gt; covers everything I needed (auth, license validation, update checks). No Alamofire.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JSON?&lt;/strong&gt; &lt;code&gt;Codable&lt;/code&gt; is built in. I haven't needed a custom JSON library since Swift 4.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keychain?&lt;/strong&gt; The &lt;code&gt;Security&lt;/code&gt; framework API is ugly, but wrapping &lt;code&gt;SecItemAdd&lt;/code&gt; / &lt;code&gt;SecItemCopyMatching&lt;/code&gt; in a 50-line helper is less work than adopting a dependency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Analytics?&lt;/strong&gt; I commented out Firebase entirely. Crash logs write to &lt;code&gt;~/Library/Logs/&lt;/code&gt; locally. I'll add opt-in analytics later if I need it, but for launch, local-only felt right.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UI?&lt;/strong&gt; SwiftUI. No third-party component libraries.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result: my &lt;code&gt;Package.swift&lt;/code&gt; looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;package&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Package&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"CleanSlateX"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="nv"&gt;dependencies&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the app builds in under 30 seconds on an M-series Mac.&lt;/p&gt;

&lt;h3&gt;
  
  
  The tradeoff nobody talks about
&lt;/h3&gt;

&lt;p&gt;Zero deps means you own every bug. When my Keychain wrapper silently failed on unsigned debug builds, there was no GitHub issue to search. I had to figure out that &lt;code&gt;SecItemAdd&lt;/code&gt; returns &lt;code&gt;-34018&lt;/code&gt; when the app isn't code-signed, and build a fallback to AES-GCM encrypted file storage.&lt;/p&gt;

&lt;p&gt;Would a Keychain library have handled that? Probably. But I would've spent the same time debugging &lt;em&gt;their&lt;/em&gt; abstraction instead of Apple's API. Pick your poison.&lt;/p&gt;

&lt;h2&gt;
  
  
  SwiftUI for a utility app: the honest review
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What worked great
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Declarative UI for data-heavy views.&lt;/strong&gt; My app shows long lists of files with sizes, checkboxes, and categories. SwiftUI's &lt;code&gt;List&lt;/code&gt; + &lt;code&gt;ForEach&lt;/code&gt; + &lt;code&gt;@Observable&lt;/code&gt; made this almost trivial.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dark mode and accessibility for free.&lt;/strong&gt; Zero custom theming code. It just works.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Small binary size.&lt;/strong&gt; SwiftUI links against system frameworks, so the app stays tiny.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What hurt
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Previews are unreliable.&lt;/strong&gt; They crash, they show stale data, they randomly stop updating. I switched to a live development workflow (build and run) for anything beyond trivial views. I know this defeats the purpose.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;NSOpenPanel&lt;/code&gt; and AppKit interop.&lt;/strong&gt; macOS SwiftUI still can't do everything. I needed &lt;code&gt;NSOpenPanel&lt;/code&gt; for folder selection, &lt;code&gt;SMJobBless&lt;/code&gt; for the privileged helper, and &lt;code&gt;NSWorkspace&lt;/code&gt; for various system interactions. Wrapping AppKit in SwiftUI is doable but feels like duct tape.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Swift Concurrency + &lt;code&gt;@MainActor&lt;/code&gt; propagation.&lt;/strong&gt; Once you mark one view model as &lt;code&gt;@MainActor&lt;/code&gt;, the annotation spreads through your codebase like a virus. Not necessarily bad, but surprising if you're used to GCD.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The privileged helper rabbit hole
&lt;/h2&gt;

&lt;p&gt;My app needs to clean system-level caches (&lt;code&gt;/Library/Caches/&lt;/code&gt;, &lt;code&gt;/Library/Logs/&lt;/code&gt;), which requires root access. On macOS, the approved way to do this is &lt;code&gt;SMJobBless&lt;/code&gt; — you create a separate helper binary that gets installed to &lt;code&gt;/Library/PrivilegedHelperTools/&lt;/code&gt; with a &lt;code&gt;launchd&lt;/code&gt; plist.&lt;/p&gt;

&lt;p&gt;What the docs don't tell you:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The helper and the app must reference each other's code signing requirements.&lt;/strong&gt; The app's Info.plist lists the helper, the helper's Info.plist lists the app. Miss one and installation silently fails.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The helper validates its caller.&lt;/strong&gt; Every XPC call, the helper checks &lt;code&gt;SecCodeCheckValidity&lt;/code&gt; against the calling app's code signing identity. This prevents other apps from talking to your helper.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;You need a command allowlist.&lt;/strong&gt; The helper runs as root. If it can execute arbitrary shell commands, you've built a privilege escalation vulnerability. I hardcoded an allowlist of specific system utilities (&lt;code&gt;tmutil&lt;/code&gt;, &lt;code&gt;mdutil&lt;/code&gt;, &lt;code&gt;dscacheutil&lt;/code&gt;, etc.) and scoped &lt;code&gt;rm -rf&lt;/code&gt; to specific directory prefixes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The user sees exactly one password prompt.&lt;/strong&gt; &lt;code&gt;AuthorizationCreate&lt;/code&gt; with &lt;code&gt;kAuthorizationFlagInteractionAllowed&lt;/code&gt; shows the standard macOS admin dialog. After that, the helper stays installed and subsequent operations don't re-prompt.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This took me longer to get right than any other feature. If you're building a macOS utility that needs elevated permissions, budget a full week for this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Notarization: the tax you have to pay
&lt;/h2&gt;

&lt;p&gt;Apple requires Developer ID apps to be notarized — essentially, you upload your signed binary to Apple, they scan it, and staple a ticket that tells Gatekeeper "this is safe."&lt;/p&gt;

&lt;p&gt;My pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Build with Hardened Runtime + timestamp&lt;/span&gt;
xcodebuild ... &lt;span class="nv"&gt;CODE_SIGN_IDENTITY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Developer ID Application"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nv"&gt;OTHER_CODE_SIGN_FLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"--timestamp --options runtime"&lt;/span&gt;

&lt;span class="c"&gt;# Submit to Apple&lt;/span&gt;
xcrun notarytool submit CleanSlateX.dmg &lt;span class="nt"&gt;--wait&lt;/span&gt; &lt;span class="nt"&gt;--apple-id&lt;/span&gt; ... &lt;span class="nt"&gt;--team-id&lt;/span&gt; ...

&lt;span class="c"&gt;# Staple the ticket&lt;/span&gt;
xcrun stapler staple CleanSlateX.app
xcrun stapler staple CleanSlateX.dmg

&lt;span class="c"&gt;# Verify&lt;/span&gt;
spctl &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-vvv&lt;/span&gt; CleanSlateX.app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The gotcha: notarization fails silently if you forget &lt;code&gt;--timestamp&lt;/code&gt; in the code signing flags. The binary looks signed, passes local verification, but Apple rejects it. This cost me half a day.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd do differently
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Start with the privileged helper on day one.&lt;/strong&gt; I bolted it on later and had to refactor the entire cleanup pipeline. Design for split-process architecture from the start if your app needs it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Don't fight Previews.&lt;/strong&gt; Set up a lightweight preview-friendly architecture early (injectable mock data, protocol-backed services), or just accept that you'll use live builds.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Invest in a proper build script early.&lt;/strong&gt; Code signing, notarization, DMG creation, version bumping — automate all of it. I have a single &lt;code&gt;build-release.sh&lt;/code&gt; that does everything now, and it's the best time investment I made.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ship the free tier first.&lt;/strong&gt; I spent too long on the payment integration before validating that people wanted the core product. The free scan-only tier taught me more about user behavior in one week than months of building.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;I'm launching on Show HN this Sunday. Honestly, I have no idea how it'll go — the "Mac cleaner" category has a lot of baggage on HN. But the app is built for developers specifically (13 dev toolchains, per-item transparency), and I think the engineering-first approach will resonate with that audience. Or they'll tell me &lt;code&gt;rm -rf&lt;/code&gt; is all anyone needs. Either way, I'll learn something.&lt;/p&gt;

&lt;p&gt;If you made it this far — what's your experience been with native macOS development? Are you still fighting AppKit, or has SwiftUI gotten good enough for your use case? I'd genuinely love to hear.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'm building CleanSlateX, a macOS utility that audits and cleans developer tool caches. You can check it out at &lt;a href="https://cleanslatex.app" rel="noopener noreferrer"&gt;cleanslatex.app&lt;/a&gt; — the scan is free, no account required.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>swift</category>
      <category>swiftui</category>
      <category>buildinpublic</category>
    </item>
    <item>
      <title>The Complete Xcode Cleanup Guide for iOS/macOS Developers — Reclaim 50-150 GB (2026)</title>
      <dc:creator>pyrx.tech</dc:creator>
      <pubDate>Tue, 19 May 2026 12:46:40 +0000</pubDate>
      <link>https://forem.com/pyrx_tech/the-complete-xcode-cleanup-guide-for-iosmacos-developers-reclaim-50-150-gb-2026-2h5j</link>
      <guid>https://forem.com/pyrx_tech/the-complete-xcode-cleanup-guide-for-iosmacos-developers-reclaim-50-150-gb-2026-2h5j</guid>
      <description>&lt;p&gt;If you've been doing iOS/macOS development for more than a year, Xcode has been quietly claiming 50-150 GB of your disk without telling you. Here's where all that space went and exactly how to get it back.&lt;/p&gt;

&lt;p&gt;I'll cover every major Xcode storage sink — DerivedData, simulators, device support, archives, SPM cache, and module cache — with the exact commands to audit and clean each one. Everything here is safe and reversible.&lt;/p&gt;

&lt;h2&gt;
  
  
  The quick audit: see your damage in 10 seconds
&lt;/h2&gt;

&lt;p&gt;Run this first to understand the scale:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"=== Xcode Storage Audit ==="&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; ~/Library/Developer/Xcode/DerivedData 2&amp;gt;/dev/null &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; ~/Library/Developer/CoreSimulator 2&amp;gt;/dev/null &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; /Library/Developer/CoreSimulator/Images 2&amp;gt;/dev/null &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; ~/Library/Developer/Xcode/Archives 2&amp;gt;/dev/null &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; ~/Library/Developer/Xcode/iOS&lt;span class="se"&gt;\ &lt;/span&gt;DeviceSupport 2&amp;gt;/dev/null &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; ~/Library/Developer/Xcode/watchOS&lt;span class="se"&gt;\ &lt;/span&gt;DeviceSupport 2&amp;gt;/dev/null &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; ~/Library/Developer/Xcode/tvOS&lt;span class="se"&gt;\ &lt;/span&gt;DeviceSupport 2&amp;gt;/dev/null &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; ~/Library/Caches/org.swift.swiftpm 2&amp;gt;/dev/null &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; ~/Library/Developer/Xcode/DerivedData/ModuleCache.noindex 2&amp;gt;/dev/null
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On my machine after 18 months of active development, the total was over 90 GB. Here's what each one is and what to do about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. DerivedData (typically 20-60 GB)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it is:&lt;/strong&gt; Xcode's intermediate build output — compiled objects, indexes, build logs, module caches. Every project gets its own subfolder.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Location:&lt;/strong&gt; &lt;code&gt;~/Library/Developer/Xcode/DerivedData/&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is it safe to delete?&lt;/strong&gt; Yes. Xcode regenerates everything on the next build. The only cost is a full rebuild instead of an incremental one. Your source code, provisioning profiles, and App Store submissions are not affected.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to clean it
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Option A — Delete everything:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; ~/Library/Developer/Xcode/DerivedData/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Option B — Delete selectively (keep active projects):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# See each project folder with size and last modified date&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-lahSt&lt;/span&gt; ~/Library/Developer/Xcode/DerivedData/

&lt;span class="c"&gt;# Delete only folders older than 30 days&lt;/span&gt;
find ~/Library/Developer/Xcode/DerivedData &lt;span class="nt"&gt;-maxdepth&lt;/span&gt; 1 &lt;span class="nt"&gt;-mtime&lt;/span&gt; +30 &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt; +
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Option C — From Xcode UI:&lt;/strong&gt;&lt;br&gt;
Xcode → Settings → Locations → click the arrow next to "Derived Data" to open in Finder. Delete what you don't need.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pro tip:&lt;/strong&gt; &lt;code&gt;Cmd+Shift+K&lt;/code&gt; in Xcode only cleans the current project's build folder. It doesn't touch other projects' DerivedData.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. Simulator runtimes (5-8 GB each)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it is:&lt;/strong&gt; Full OS images for every iOS/watchOS/tvOS version you've ever downloaded. Each one is 5-8 GB.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where they live:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User runtimes: &lt;code&gt;~/Library/Developer/CoreSimulator/Profiles/Runtimes/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;System images (Xcode 15+): &lt;code&gt;/Library/Developer/CoreSimulator/Images/&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  How to clean them
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# List all installed runtimes with sizes&lt;/span&gt;
xcrun simctl runtime list

&lt;span class="c"&gt;# Preview what would be deleted (dry run — deletes nothing)&lt;/span&gt;
xcrun simctl runtime delete &lt;span class="nt"&gt;--notUsedSinceDays&lt;/span&gt; 180 &lt;span class="nt"&gt;--dry-run&lt;/span&gt;

&lt;span class="c"&gt;# Actually delete runtimes not used in 6 months&lt;/span&gt;
xcrun simctl runtime delete &lt;span class="nt"&gt;--notUsedSinceDays&lt;/span&gt; 180

&lt;span class="c"&gt;# Nuclear option: delete all unavailable/orphaned simulators&lt;/span&gt;
xcrun simctl delete unavailable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Or via Xcode:&lt;/strong&gt; Settings → Platforms → click the minus button on runtimes you don't need.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rule of thumb:&lt;/strong&gt; Keep only the 2-3 most recent iOS versions you actively test against. You can always re-download a runtime if you need it.&lt;/p&gt;
&lt;h2&gt;
  
  
  3. Device support files (1-5 GB per iOS version)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it is:&lt;/strong&gt; Debug symbols generated every time you plug in a physical device running a specific iOS version. These accumulate for every iOS version you've ever connected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Location:&lt;/strong&gt; &lt;code&gt;~/Library/Developer/Xcode/iOS DeviceSupport/&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  How to clean them
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# See all device support folders with sizes&lt;/span&gt;
&lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; ~/Library/Developer/Xcode/iOS&lt;span class="se"&gt;\ &lt;/span&gt;DeviceSupport/&lt;span class="k"&gt;*&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-hr&lt;/span&gt;

&lt;span class="c"&gt;# Delete support files for iOS versions you no longer target&lt;/span&gt;
&lt;span class="c"&gt;# Example: delete everything older than iOS 17&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; ~/Library/Developer/Xcode/iOS&lt;span class="se"&gt;\ &lt;/span&gt;DeviceSupport/ | &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;read &lt;/span&gt;d&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nv"&gt;version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$d&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-oE&lt;/span&gt; &lt;span class="s1"&gt;'^[0-9]+'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$version&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-lt&lt;/span&gt; 17 &lt;span class="o"&gt;]&lt;/span&gt; 2&amp;gt;/dev/null&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Removing: &lt;/span&gt;&lt;span class="nv"&gt;$d&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; ~/Library/Developer/Xcode/iOS&lt;span class="se"&gt;\ &lt;/span&gt;DeviceSupport/&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$d&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;fi
done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Also check &lt;code&gt;watchOS DeviceSupport&lt;/code&gt; and &lt;code&gt;tvOS DeviceSupport&lt;/code&gt; if you've ever connected an Apple Watch or Apple TV.&lt;/p&gt;
&lt;h2&gt;
  
  
  4. Xcode archives (2-10 GB per archive)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it is:&lt;/strong&gt; &lt;code&gt;.xcarchive&lt;/code&gt; bundles from every App Store submission or export you've ever done.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Location:&lt;/strong&gt; &lt;code&gt;~/Library/Developer/Xcode/Archives/&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  How to clean them
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;From Xcode:&lt;/strong&gt; Window → Organizer → Archives tab → right-click old archives → Delete.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;From Terminal:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# See archive sizes grouped by year&lt;/span&gt;
&lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; ~/Library/Developer/Xcode/Archives/&lt;span class="k"&gt;*&lt;/span&gt;

&lt;span class="c"&gt;# Delete archives older than 1 year&lt;/span&gt;
find ~/Library/Developer/Xcode/Archives &lt;span class="nt"&gt;-maxdepth&lt;/span&gt; 1 &lt;span class="nt"&gt;-mtime&lt;/span&gt; +365 &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt; +
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Keep in mind:&lt;/strong&gt; You only need archives if you want to upload a build to App Store Connect, generate dSYMs, or re-export an IPA. If the app version is already live and you have dSYMs uploaded, the archive is safe to delete.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. SPM cache (3-8 GB)
&lt;/h2&gt;

&lt;p&gt;This one is sneaky — Swift Package Manager stores resolved packages in a cache directory that grows silently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Location:&lt;/strong&gt; &lt;code&gt;~/Library/Caches/org.swift.swiftpm/&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How to clean it
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check size&lt;/span&gt;
&lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; ~/Library/Caches/org.swift.swiftpm/

&lt;span class="c"&gt;# Delete the global cache&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; ~/Library/Caches/org.swift.swiftpm/

&lt;span class="c"&gt;# Per-project cleanup&lt;/span&gt;
swift package purge-cache
swift package reset
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The stale references gotcha:&lt;/strong&gt; Xcode sometimes caches resolved package versions in DerivedData even after you've updated dependencies. If you see version mismatch issues after updating a package:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Close Xcode completely&lt;/li&gt;
&lt;li&gt;Delete &lt;code&gt;~/Library/Caches/org.swift.swiftpm/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Delete the project's DerivedData folder&lt;/li&gt;
&lt;li&gt;Delete &lt;code&gt;Package.resolved&lt;/code&gt; from your project&lt;/li&gt;
&lt;li&gt;Reopen Xcode and let it re-resolve from scratch&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Unlike Cargo (&lt;code&gt;cargo cache --autoclean-expensive&lt;/code&gt;) or pip (&lt;code&gt;pip cache purge&lt;/code&gt;), SPM has no built-in cache size management. It's a genuine gap in the tooling.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Module cache
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it is:&lt;/strong&gt; Precompiled module files shared across all Xcode builds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Location:&lt;/strong&gt; &lt;code&gt;~/Library/Developer/Xcode/DerivedData/ModuleCache.noindex/&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check size&lt;/span&gt;
&lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; ~/Library/Developer/Xcode/DerivedData/ModuleCache.noindex/

&lt;span class="c"&gt;# Delete it (regenerated automatically)&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; ~/Library/Developer/Xcode/DerivedData/ModuleCache.noindex/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is typically 1-3 GB and gets regenerated on the next build. Safe to delete anytime.&lt;/p&gt;

&lt;h2&gt;
  
  
  The complete cleanup script
&lt;/h2&gt;

&lt;p&gt;Here's a single script that audits everything, shows you the totals, and lets you decide what to remove:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"=== Xcode Disk Space Audit ==="&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;

&lt;span class="nv"&gt;paths&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;
  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/Library/Developer/Xcode/DerivedData"&lt;/span&gt;
  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/Library/Developer/CoreSimulator"&lt;/span&gt;
  &lt;span class="s2"&gt;"/Library/Developer/CoreSimulator/Images"&lt;/span&gt;
  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/Library/Developer/Xcode/Archives"&lt;/span&gt;
  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/Library/Developer/Xcode/iOS DeviceSupport"&lt;/span&gt;
  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/Library/Developer/Xcode/watchOS DeviceSupport"&lt;/span&gt;
  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/Library/Developer/Xcode/tvOS DeviceSupport"&lt;/span&gt;
  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/Library/Caches/org.swift.swiftpm"&lt;/span&gt;
  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/Library/Developer/Xcode/DerivedData/ModuleCache.noindex"&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;total&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
&lt;span class="k"&gt;for &lt;/span&gt;p &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$p&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nv"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-sm&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$p&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;/dev/null | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-f1&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
    &lt;span class="nv"&gt;total&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;total &lt;span class="o"&gt;+&lt;/span&gt; size&lt;span class="k"&gt;))&lt;/span&gt;
    &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"%6s MB  %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$size&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$p&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;fi
done

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Total: &lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;total &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="m"&gt;1024&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="s2"&gt; GB reclaimable"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"To clean DerivedData:    rm -rf ~/Library/Developer/Xcode/DerivedData/*"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"To clean simulators:     xcrun simctl runtime delete --notUsedSinceDays 180"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"To clean SPM cache:      rm -rf ~/Library/Caches/org.swift.swiftpm/"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"To clean module cache:   rm -rf ~/Library/Developer/Xcode/DerivedData/ModuleCache.noindex/"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"To clean archives:       Open Xcode &amp;gt; Window &amp;gt; Organizer &amp;gt; delete old archives"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"To clean device support: Manually delete old iOS versions from ~/Library/Developer/Xcode/iOS DeviceSupport/"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Preventing future bloat
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;After every major Xcode update:&lt;/strong&gt; review simulators (Settings → Platforms) and delete old runtimes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monthly:&lt;/strong&gt; run the audit script above&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;After shipping a release:&lt;/strong&gt; delete the archive if you've already uploaded dSYMs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consider a cron job&lt;/strong&gt; for DerivedData older than 30 days:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="c"&gt;# Add to crontab (crontab -e)&lt;/span&gt;
   0 3 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; 0 find ~/Library/Developer/Xcode/DerivedData &lt;span class="nt"&gt;-maxdepth&lt;/span&gt; 1 &lt;span class="nt"&gt;-mtime&lt;/span&gt; +30 &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt; +
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Beyond Xcode
&lt;/h2&gt;

&lt;p&gt;If you also work with Docker, Node.js, Homebrew, Python, or other toolchains, those have their own cache bloat problems. On a multi-toolchain developer Mac, total reclaimable space can easily exceed 100 GB.&lt;/p&gt;

&lt;p&gt;Disclosure: I'm building &lt;a href="https://cleanslatex.app" rel="noopener noreferrer"&gt;CleanSlateX&lt;/a&gt;, a macOS app that automates this audit across 13 dev toolchains and shows you every file before it deletes anything — nothing is removed without your approval. But the terminal commands above cover everything you need for Xcode specifically.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;What's your DerivedData damage? Run the audit script and share your number in the comments — I'm curious how bad it gets across different setups.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ios</category>
      <category>swift</category>
      <category>tutorial</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
