<?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>DEV Community: Amar Singh</title>
    <description>The latest articles on DEV Community by Amar Singh (@amar_singh_056512a73ef40e).</description>
    <link>https://dev.to/amar_singh_056512a73ef40e</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4197647%2F2670189a-4bb5-471d-a0c9-f14271049880.png</url>
      <title>DEV Community: Amar Singh</title>
      <link>https://dev.to/amar_singh_056512a73ef40e</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/amar_singh_056512a73ef40e"/>
    <language>en</language>
    <item>
      <title>I built a free, open-source Mac app in SwiftUI. Its disk scanner is 3 faster than du</title>
      <dc:creator>Amar Singh</dc:creator>
      <pubDate>Wed, 21 Oct 2026 07:15:25 +0000</pubDate>
      <link>https://dev.to/amar_singh_056512a73ef40e/i-built-a-free-open-source-mac-app-in-swiftui-its-disk-scanner-is-3x-faster-than-du-6jo</link>
      <guid>https://dev.to/amar_singh_056512a73ef40e/i-built-a-free-open-source-mac-app-in-swiftui-its-disk-scanner-is-3x-faster-than-du-6jo</guid>
      <description>&lt;p&gt;My Mac kept saying "Storage almost full", and I wanted to know &lt;em&gt;why&lt;/em&gt;. While I was at it, I wanted answers to a few&lt;br&gt;
other questions macOS never really answers: which apps used my camera last week? What starts by itself when I log&lt;br&gt;
in? Which app is quietly draining my battery?&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;Sweep&lt;/strong&gt;, a native macOS app that cleans, protects and explains your Mac. It's free and open source&lt;br&gt;
(GPL-3.0), and this post is about how it works under the hood.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/amar97march" rel="noopener noreferrer"&gt;
        amar97march
      &lt;/a&gt; / &lt;a href="https://github.com/amar97march/sweep" rel="noopener noreferrer"&gt;
        sweep
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Clean, protect and understand your Mac. Free and open source
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;p&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/amar97march/sweep/docs/assets/banner.jpg"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Famar97march%2Fsweep%2FHEAD%2Fdocs%2Fassets%2Fbanner.jpg" alt="Sweep: more than a cleaner" width="100%"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Clean, protect and understand your Mac. Free and open source.&lt;/h3&gt;
&lt;/div&gt;

&lt;p&gt;
  &lt;a href="https://github.com/amar97march/sweep/../../releases/latest" rel="noopener noreferrer"&gt;&lt;img alt="Download for macOS" src="https://camo.githubusercontent.com/8911f73e88dccd88a57508bdc709dd47c5e96c050c89c791e5df6238a14c60d1/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f446f776e6c6f61642d6d61634f532d3146443842343f7374796c653d666f722d7468652d6261646765266c6f676f3d6170706c65266c6f676f436f6c6f723d7768697465"&gt;&lt;/a&gt;
  &lt;a href="https://youtu.be/7XYMyBQJFTM" rel="nofollow noopener noreferrer"&gt;&lt;img alt="Watch the demo" src="https://camo.githubusercontent.com/9e241b846beca5e33ef342a9f03fc30bb2648fa73d4fe70b59194e1ba4f04020/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f57617463682d31253341343525323064656d6f2d4646303030303f7374796c653d666f722d7468652d6261646765266c6f676f3d796f7574756265266c6f676f436f6c6f723d7768697465"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/2ee6bc43ed245d7883ee500cd07969d03cb2bc88f81722c8d28e616a8b923a51/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6d61634f532d31342532422d3131313131313f6c6f676f3d6170706c65"&gt;&lt;img alt="macOS 14 or later" src="https://camo.githubusercontent.com/2ee6bc43ed245d7883ee500cd07969d03cb2bc88f81722c8d28e616a8b923a51/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6d61634f532d31342532422d3131313131313f6c6f676f3d6170706c65"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/74022f2db1ca9c8192965493cdb189f6fee8fb4d14809c73c53456c0d6e31108/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f53776966742d362d4630353133383f6c6f676f3d7377696674266c6f676f436f6c6f723d7768697465"&gt;&lt;img alt="Swift 6" src="https://camo.githubusercontent.com/74022f2db1ca9c8192965493cdb189f6fee8fb4d14809c73c53456c0d6e31108/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f53776966742d362d4630353133383f6c6f676f3d7377696674266c6f676f436f6c6f723d7768697465"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/amar97march/sweep/LICENSE" rel="noopener noreferrer"&gt;&lt;img alt="Licence: GPL-3.0" src="https://camo.githubusercontent.com/2692a939816b5e50f74017f13c80d3d328ff52841bfd73be3f6dcb69e6812909/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e63652d47504c2d2d332e302d304539464438"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/6f92b6ca09da4eff36aa01b09064b118b775ddd64b706d38e128db667a8331d8/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f747261636b696e672d6e6f6e652d314644384234"&gt;&lt;img alt="No tracking" src="https://camo.githubusercontent.com/6f92b6ca09da4eff36aa01b09064b118b775ddd64b706d38e128db667a8331d8/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f747261636b696e672d6e6f6e652d314644384234"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
  &lt;a href="https://github.com/amar97march/sweep#install" rel="noopener noreferrer"&gt;&lt;b&gt;Install&lt;/b&gt;&lt;/a&gt; ·
  &lt;a href="https://github.com/amar97march/sweep#features" rel="noopener noreferrer"&gt;Features&lt;/a&gt; ·
  &lt;a href="https://github.com/amar97march/sweep#safe-by-design" rel="noopener noreferrer"&gt;Safe by design&lt;/a&gt; ·
  &lt;a href="https://github.com/amar97march/sweep#build-from-source" rel="noopener noreferrer"&gt;Build from source&lt;/a&gt; ·
  &lt;a href="https://github.com/amar97march/sweep/CONTRIBUTING.md" rel="noopener noreferrer"&gt;Contribute&lt;/a&gt; ·
  &lt;a href="https://github.com/amar97march/sweep#support-sweep" rel="noopener noreferrer"&gt;Support&lt;/a&gt;
&lt;/p&gt;




&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;
  &lt;a href="https://youtu.be/7XYMyBQJFTM" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Famar97march%2Fsweep%2FHEAD%2Fdocs%2Fassets%2Fdemo-thumbnail.jpg" alt="Watch the 1:45 demo on YouTube" width="88%"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;One checkup for your whole Mac&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/amar97march/sweep/docs/assets/checkup-scan.gif"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fraw.githubusercontent.com%2Famar97march%2Fsweep%2FHEAD%2Fdocs%2Fassets%2Fcheckup-scan.gif" alt="Checkup scanning junk, security, maintenance, apps and clutter" width="100%"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Press &lt;strong&gt;Scan&lt;/strong&gt; 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.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Features&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Junk Files: gigabytes back, safely&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/amar97march/sweep/docs/screenshots/junk-files-review.jpg"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Famar97march%2Fsweep%2FHEAD%2Fdocs%2Fscreenshots%2Fjunk-files-review.jpg" alt="Junk Files review" width="100%"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;App and system caches, logs and crash reports, the Trash, old iPhone backups and downloaded installers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developer junk&lt;/strong&gt;: Xcode…&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/amar97march/sweep" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/7XYMyBQJFTM" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnd8lag92c3fics106gyh.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnd8lag92c3fics106gyh.gif" alt="Checkup scanning junk, security, maintenance and apps" width="600" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Junk Files&lt;/strong&gt;: 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.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Disk Map&lt;/strong&gt;: a live bubble map of any folder, plus &lt;strong&gt;What Grew&lt;/strong&gt; since your last scan.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Activity&lt;/strong&gt;: 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.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt;: a malware scan using Apple's own XProtect rules, risky startup items, and a quarantine you can restore from.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Health&lt;/strong&gt;: a 0–100 score with reasons, battery care, SSD health and a security checklist.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Updates&lt;/strong&gt;: every app update from developers, the App Store and Homebrew, in one list.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There's no account and no analytics, and nothing is ever removed until you've reviewed it.&lt;/p&gt;

&lt;p&gt;The rest of this post covers the parts I enjoyed building most.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Scanning a disk 3× faster than &lt;code&gt;du&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The obvious way to measure a folder is to list it, then call &lt;code&gt;stat&lt;/code&gt; on every file. On a big tree, that's hundreds of&lt;br&gt;
thousands of system calls.&lt;/p&gt;

&lt;p&gt;macOS has a better API: &lt;code&gt;getattrlistbulk(2)&lt;/code&gt;.&lt;br&gt;
You open a directory, say which attributes you want, and each call fills a buffer with packed records for &lt;em&gt;many&lt;/em&gt;&lt;br&gt;
entries at once: name, type, flags, inode number, link count and allocated size.&lt;/p&gt;

&lt;p&gt;This is the core loop of Sweep's directory reader, slightly simplified:&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;fd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;O_RDONLY&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;O_DIRECTORY&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;O_NOFOLLOW&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;O_CLOEXEC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="n"&gt;fd&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;errno&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;errno&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// One call fills a 256 KiB buffer with records for a few thousand entries.&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getattrlistbulk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bufferSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;                  &lt;span class="c1"&gt;// finished&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;errno&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;errno&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;record&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;UnsafeRawPointer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loadUnaligned&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;as&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;UInt32&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;body&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;                       &lt;span class="c1"&gt;// decode one packed record&lt;/span&gt;
        &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The records are tightly packed. Attributes come in a fixed order, each aligned to 4 bytes, and only if they appear&lt;br&gt;
in the "returned attributes" set at the start of the record. Names are stored as an offset and a length&lt;br&gt;
(&lt;code&gt;attrreference_t&lt;/code&gt;). Getting &lt;code&gt;parse&lt;/code&gt; right took careful reading of &lt;code&gt;getattrlist(2)&lt;/code&gt;, and a lot of unit tests.&lt;/p&gt;

&lt;p&gt;On top of the reader sits a fixed pool of threads (twice the number of cores, at most 32) sharing one LIFO queue of&lt;br&gt;
folders. A LIFO queue makes the walk roughly depth-first, so the list of folders waiting to be read stays small.&lt;/p&gt;

&lt;p&gt;Measured on an M-series Mac with a warm cache, with totals that match &lt;code&gt;du&lt;/code&gt; byte for byte:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Folder&lt;/th&gt;
&lt;th&gt;Entries&lt;/th&gt;
&lt;th&gt;&lt;code&gt;du -sk&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;Sweep&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/Applications&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;575 k&lt;/td&gt;
&lt;td&gt;4.6 s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.4 s&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/opt/homebrew&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;446 k&lt;/td&gt;
&lt;td&gt;3.3 s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.0 s&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/Library&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;127 k&lt;/td&gt;
&lt;td&gt;0.8 s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.5 s&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  2. Sizes that are actually right
&lt;/h2&gt;

&lt;p&gt;Fast doesn't matter if the numbers are wrong, so the scanner follows a few strict rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bytes allocated on disk&lt;/strong&gt;, not file length, the same as &lt;code&gt;du&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hard links counted once&lt;/strong&gt;, using the inode number and link count.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never crossing into other volumes.&lt;/strong&gt; When scanning &lt;code&gt;/&lt;/code&gt;, it also skips &lt;code&gt;/System/Volumes/Data&lt;/code&gt;, so firmlinked data
isn't counted twice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never following symlinks.&lt;/strong&gt; &lt;code&gt;O_NOFOLLOW&lt;/code&gt; on every open, and symlinks are reported as entries of their own.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never downloading iCloud files.&lt;/strong&gt; 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:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="nf"&gt;setiopolicy_np&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;IOPOL_TYPE_VFS_MATERIALIZE_DATALESS_FILES&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
               &lt;span class="kt"&gt;IOPOL_SCOPE_THREAD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
               &lt;span class="kt"&gt;IOPOL_MATERIALIZE_DATALESS_FILES_OFF&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The command-line tool also has a &lt;code&gt;--private-size&lt;/code&gt; option that reports what deleting a file would &lt;em&gt;really&lt;/em&gt; free when&lt;br&gt;
APFS clones share its blocks.&lt;/p&gt;
&lt;h2&gt;
  
  
  3. A cleaner that's careful about deleting
&lt;/h2&gt;

&lt;p&gt;The worst bug a cleaner can have is deleting the wrong thing. So every removal in Sweep goes through one&lt;br&gt;
&lt;code&gt;FileRemover&lt;/code&gt;, with a &lt;code&gt;RemovalPolicy&lt;/code&gt; that refuses anything risky: paths that aren't plain absolute paths, system&lt;br&gt;
folders like &lt;code&gt;/System/&lt;/code&gt; and &lt;code&gt;/usr/bin/&lt;/code&gt;, mount points, and folders that must exist (it removes their contents instead).&lt;/p&gt;

&lt;p&gt;The tests use the same remover, locked to a sandbox folder:&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="c1"&gt;/// The standard rules, plus: nothing outside `roots` may be touched.&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;confined&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nv"&gt;roots&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;RemovalPolicy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;policy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;standard&lt;/span&gt;
    &lt;span class="n"&gt;policy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;allowedRoots&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;roots&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All of the nearly 300 tests build their files inside the build folder, use a stand-in Trash, and keep settings in&lt;br&gt;
memory. A test run can't delete, move or change anything outside the project: not your files, not the real Trash.&lt;/p&gt;

&lt;p&gt;In the app, every scan ends in a review list you can check, change or cancel, removals are logged, and the Trash is&lt;br&gt;
the default for anything you might want back.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Reading what macOS already knows
&lt;/h2&gt;

&lt;p&gt;Most of the "explain your Mac" features don't guess. They read data macOS already keeps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Malware scan&lt;/strong&gt;: Sweep runs Apple's own XProtect YARA rules in place, plus any &lt;code&gt;.yar&lt;/code&gt; rules you add, with its own
YARA engine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Camera and microphone history&lt;/strong&gt;: read from the log behind the privacy indicator (the green and orange dots in the
menu bar), with 90 days of history.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Drive health&lt;/strong&gt;: 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.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Battery charge limit&lt;/strong&gt;: read from the power management policy macOS publishes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Intel-only apps&lt;/strong&gt;: found by reading each app's Mach-O header, so you're warned before Rosetta goes away.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Updates&lt;/strong&gt;: each app's own update feed, with the EdDSA signature and the developer team checked, plus the App
Store and Homebrew.&lt;/li&gt;
&lt;/ul&gt;

&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2b8re37h7e0d87eme8o0.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2b8re37h7e0d87eme8o0.jpg" alt="Apps that can see what you type, and every permission you've given" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5. A SwiftUI interface that moves, but not too much
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fggdcvz7rwqhcpluz4th5.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fggdcvz7rwqhcpluz4th5.gif" alt="Disk Map: hovering bubbles and opening a folder" width="560" height="315"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The whole app is SwiftUI in Swift 6 language mode, so the compiler checks concurrency for data races. The window is&lt;br&gt;
built from a small set of components: a drifting mesh-gradient background with a starfield (macOS 15, with a softer&lt;br&gt;
fallback on macOS 14), glass cards, a comet-style progress ring, and cards that tilt toward the pointer. Disk Map lays&lt;br&gt;
out its bubbles with circle packing.&lt;/p&gt;

&lt;p&gt;Two rules kept it from getting out of hand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Every continuous animation stops when Reduce Motion is on.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Every screen is checked automatically.&lt;/strong&gt; 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.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Download&lt;/strong&gt; the DMG from &lt;a href="https://github.com/amar97march/sweep/releases/latest" rel="noopener noreferrer"&gt;Releases&lt;/a&gt; (macOS 14 or later).
It isn't notarized yet, so the first time you open it, go to &lt;strong&gt;System Settings → Privacy &amp;amp; Security&lt;/strong&gt; and click
&lt;strong&gt;Open Anyway&lt;/strong&gt;. The full source is public if you'd like to read what it does first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build it yourself&lt;/strong&gt; with Xcode 16 or later:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/amar97march/sweep.git
&lt;span class="nb"&gt;cd &lt;/span&gt;sweep
swift &lt;span class="nb"&gt;test
&lt;/span&gt;scripts/make-app.sh &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; open .build/Sweep.app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Help wanted
&lt;/h2&gt;

&lt;p&gt;Sweep is a one-person project so far, and contributions are very welcome. A few good places to start:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Live bubbles in Disk Map while a scan is still running&lt;/li&gt;
&lt;li&gt;Translations&lt;/li&gt;
&lt;li&gt;The caches of a developer tool you use&lt;/li&gt;
&lt;li&gt;A VoiceOver pass over every screen&lt;/li&gt;
&lt;li&gt;A Homebrew tap, so &lt;code&gt;brew install&lt;/code&gt; works&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If Sweep is useful to you, a ⭐ on &lt;a href="https://github.com/amar97march/sweep" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; really helps other people find it,&lt;br&gt;
and you can &lt;a href="https://buymeacoffee.com/amar97march" rel="noopener noreferrer"&gt;buy me a coffee&lt;/a&gt; to support the work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's the biggest surprise hiding in your Mac's storage? And what would you want an app like this to explain&lt;br&gt;
next?&lt;/strong&gt; I'd love to hear in the comments.&lt;/p&gt;

</description>
      <category>swift</category>
      <category>macos</category>
      <category>opensource</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
