<?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: Vishwas Prakash</title>
    <description>The latest articles on DEV Community by Vishwas Prakash (@vishwas3000).</description>
    <link>https://dev.to/vishwas3000</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%2F4104368%2F405fa731-4179-4c2e-bd8e-00cf5d53b582.jpg</url>
      <title>DEV Community: Vishwas Prakash</title>
      <link>https://dev.to/vishwas3000</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vishwas3000"/>
    <language>en</language>
    <item>
      <title>The Mac got slower, and quitting the app didn't fix it</title>
      <dc:creator>Vishwas Prakash</dc:creator>
      <pubDate>Tue, 01 Sep 2026 14:48:35 +0000</pubDate>
      <link>https://dev.to/vishwas3000/the-mac-got-slower-and-quitting-the-app-didnt-fix-it-1jod</link>
      <guid>https://dev.to/vishwas3000/the-mac-got-slower-and-quitting-the-app-didnt-fix-it-1jod</guid>
      <description>&lt;p&gt;This is the worst bug I have written. Not because the app broke — it worked fine throughout — but because it degraded the machine it ran on, permanently, and left the damage behind after quitting. Someone could have uninstalled the app and still been slow, with nothing to connect the two.&lt;/p&gt;

&lt;p&gt;ExtendPilot puts a real second desktop on an iPad: not a mirror, but a display macOS treats as hardware, arrangeable in System Settings, with windows that move onto it. There is no public API for that, so it sits on a private one. This is the story of what the private one does when you hold it wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it looked like
&lt;/h2&gt;

&lt;p&gt;After a day of development the Mac had a load average around 6 and felt unusable. Three ColorSync daemons were burning about 115% CPU between them — over a full core — and had been for 47 minutes at the point of measurement.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;top &lt;span class="nt"&gt;-l&lt;/span&gt; 2 &lt;span class="nt"&gt;-o&lt;/span&gt; cpu
&lt;span class="go"&gt;colorsyncd                 46.5%
colorsync.displayservices  35.6%
colorsync.useragent        33.2%
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;They were not logging anything. A full &lt;code&gt;log show&lt;/code&gt; came back empty, so nothing pointed at them at all. Two of the three run as root; sampling the one running as the user gave the hot stack immediately.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sample 96211 3
&lt;span class="go"&gt;  21  ColorSyncXPCDeviceRegistryCopyAnyUserInfo
  20  ColorSyncDeviceRegistryCopyInfo
  11  ColorSyncIterateDeviceProfiles
  10  ColorSyncProfileCreateWithURLAndOptions
   7  ColorSyncCreateCFDataFromURL
  40  __CFBinaryPlistCreateObjectFiltered
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Iterating the display device registry and re-reading profiles off disk, in a loop. So the question became how big the registry had got.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt; /Library/ColorSync/Profiles/Displays/ | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;
&lt;span class="go"&gt;97
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt; /Library/ColorSync/Profiles/Displays/ | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; LocalScreenShare
&lt;span class="go"&gt;72
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;find /Library/ColorSync/Profiles/Displays/ &lt;span class="nt"&gt;-mtime&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt; | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;
&lt;span class="go"&gt;77
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Seventy-two of ninety-seven profiles were ours, and seventy-seven of them had been created that day. Every one was an ICC profile that &lt;code&gt;colorsyncd&lt;/code&gt; had written for a monitor it had never seen before. We had been handing it a brand new monitor every few minutes, and it had been dutifully profiling each one and keeping the result forever.&lt;/p&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%2Fu9ay163779dcdtiw7uim.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu9ay163779dcdtiw7uim.png" alt="A step chart of ColorSync profiles written during one session, climbing to 72. The line does not fall at the marker where the app was quit."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The line not falling at the quit marker is the part that made this hard to find. Every instinct says a leak ends when the process does, so the machine stayed slow long after the obvious suspect had exited.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The cause is one line
&lt;/h2&gt;

&lt;p&gt;A virtual display is created from a descriptor. Three of its fields build the display's identity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight objective_c"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;uint32_t&lt;/span&gt; &lt;span class="n"&gt;nextIndex&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="n"&gt;descriptor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;productID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mh"&gt;0x4C53&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;descriptor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vendorID&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mh"&gt;0x3456&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;descriptor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;serialNum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;getpid&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nextIndex&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mh"&gt;0xFF&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;vendorID&lt;/code&gt; / &lt;code&gt;productID&lt;/code&gt; / &lt;code&gt;serialNum&lt;/code&gt; triple &lt;em&gt;is&lt;/em&gt; a display's identity to macOS. Mixing in &lt;code&gt;getpid()&lt;/code&gt; meant that identity changed on every launch. macOS concluded, correctly, that it had never seen this monitor before, and &lt;code&gt;colorsyncd&lt;/code&gt; did exactly what it is supposed to do for a new monitor: profile it, and keep the profile. Around 36 launches, two desktops each, is 72 profiles.&lt;/p&gt;

&lt;p&gt;The bitter part is that this line was itself a fix. An earlier version gave every display the same serial number, and macOS then treated two simultaneous desktops as one monitor — each inheriting the other's saved arrangement and coming up at the wrong size. Adding the pid made them distinct, and it worked. It also made them distinct &lt;em&gt;across time&lt;/em&gt;, which nobody had asked for and nobody noticed.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;An identity that had to be &lt;strong&gt;stable&lt;/strong&gt; was derived from something &lt;strong&gt;ephemeral&lt;/strong&gt;. The requirement was "distinct from its siblings". The implementation delivered "distinct from everything, forever".&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The first fix was not enough
&lt;/h2&gt;

&lt;p&gt;Replacing the pid with a hash of the display's own size and slot fixes the across-launch case, because both of those are properties of the desktop rather than of the process that made it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight objective_c"&gt;&lt;code&gt;&lt;span class="n"&gt;descriptor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;serialNum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mh"&gt;0x1FFF&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mh"&gt;0x1FFF&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;slot&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But &lt;code&gt;slot&lt;/code&gt; still came from &lt;code&gt;nextIndex++&lt;/code&gt;, a counter that only ever goes up. Within a single session, removing a desktop and adding another advanced it — and applying a saved preset does exactly that, removing every desktop and recreating them. Applying a preset three times minted six identities without the app ever restarting.&lt;/p&gt;

&lt;p&gt;The file timestamps said so plainly: profiles written at 00:45, 00:58, 01:04, 01:06 and 01:13. Minutes apart, inside one session. The fix is that the slot is now passed in by the caller, which already knows each desktop's position in its own list — it does not need a counter to tell it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A counter that only increments is the subtle version of this bug. It looks stable because it is deterministic. A counter is a clock in disguise.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Neither was the second
&lt;/h2&gt;

&lt;p&gt;Creating a display can fail if another process already holds that identity, so there is a fallback path. In the same commit that fixed the two problems above, the fallback read:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight objective_c"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;descriptor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;serialNum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;getpid&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;slot&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mh"&gt;0xFF&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// ← pid again&lt;/span&gt;
    &lt;span class="err"&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 same mistake, a third time, written by someone who had spent the previous hour understanding why it was a mistake. It fires rarely, but when it fires it is unbounded. It is now four bounded alternates, so a given size costs at most four profiles ever, rather than one per launch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight objective_c"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uint32_t&lt;/span&gt; &lt;span class="n"&gt;attempt&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="n"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;descriptor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;serialNum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mh"&gt;0x1FFF&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mh"&gt;0x1FFF&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                         &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;slot&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mh"&gt;0x0F&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="n"&gt;displayClass&lt;/span&gt; &lt;span class="nf"&gt;alloc&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="nf"&gt;initWithDescriptor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;descriptor&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;Three passes to get one line right. Worth remembering the next time a fix feels obvious.&lt;/p&gt;

&lt;h2&gt;
  
  
  A postmortem without a check is just a story
&lt;/h2&gt;

&lt;p&gt;The regression check creates the two desktops, releases them, counts the profiles on disk, creates the same two again, releases them, and counts again. If a repeat run ever mints a new identity, the number moves and the check fails.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;display profiles on disk: 27 before the repeat, 27 after
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Writing that check turned up a second bug worth recording, because it is a good example of a test being right for the wrong reason. The first version passed the displays back through out-parameters. Under ARC those are &lt;code&gt;__autoreleasing&lt;/code&gt;, so the pool held the displays alive well past the point where the test released them, and the check failed with &lt;em&gt;"a virtual display outlived its owner"&lt;/em&gt;. The check was correct. The check's own helper was the thing leaking. Each round now gets its own &lt;code&gt;@autoreleasepool&lt;/code&gt;, and there is a comment next to it explaining why no helper exists.&lt;/p&gt;

&lt;p&gt;The code fix stops new profiles. It cannot remove the seventy-two already written, which are root-owned and have to be deleted by hand. That asymmetry is the part I would most like other people to take away from this: the damage outlived the process, the fix does not reach backwards, and no amount of care after the fact recovers it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to look for in your own code
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Identity derived from something ephemeral.&lt;/strong&gt; Anything the OS persists or registers under a key you generate must have that key derived &lt;em&gt;only&lt;/em&gt; from what genuinely distinguishes the thing. Audit any use of &lt;code&gt;getpid()&lt;/code&gt;, a timestamp, a UUID, a random number or a monotonic counter that feeds a serial number, a filename, a bundle id, a service name, a Keychain account, a Bonjour name or a cache key.&lt;/p&gt;

&lt;p&gt;The question that finds it: &lt;em&gt;if this runs a thousand times, how many distinct values does the OS end up storing?&lt;/em&gt; If the answer is a thousand, this is your bug.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resources the OS keeps after you exit.&lt;/strong&gt; Virtual displays go away when released; the profiles do not. Before using an API that registers something system-wide, ask what survives the process, and whether anything ever cleans it up. In our case nothing did, and still does not — the fix is that we stop creating them, not that we tidy up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Silent hot loops.&lt;/strong&gt; &lt;code&gt;colorsyncd&lt;/code&gt; logged nothing at all while spinning. The only thing that found it was sampling the hot stack. When something is eating CPU and the logs are quiet, sample it rather than reasoning about it. And note that &lt;code&gt;ps&lt;/code&gt; reports &lt;code&gt;%CPU&lt;/code&gt; as a &lt;em&gt;lifetime average&lt;/em&gt; rather than a current reading — a 47-minute-old process showing 73% in &lt;code&gt;top -l 2&lt;/code&gt; has been there the whole time, which is a far stronger signal than a spike.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;&lt;a href="https://extendpilot.com" rel="noopener noreferrer"&gt;ExtendPilot&lt;/a&gt; shares a Mac's screen with the iPhones and iPads already in the room — as a mirror, or as a second desktop macOS treats as real hardware. Peer to peer over your own Wi-Fi, no account, no server in a session, so it works with the router unplugged. Built for the meeting room whose HDMI cable never works, and for the iPad on the desk doing nothing. The Mac app is a free notarised download, or &lt;code&gt;brew install vishwas3000/tap/extendpilot&lt;/code&gt;; the iPhone and iPad app is with App Review. Every performance claim about it traces to a session like the one above, written down with the number that was true before and the number that was true after. So do the mistakes; this is one of them.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>debugging</category>
      <category>performance</category>
      <category>macos</category>
      <category>swift</category>
    </item>
  </channel>
</rss>
