<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
     xmlns:content="http://purl.org/rss/1.0/modules/content/"
     xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>MITM LLC Blog</title>
    <link>https://mitmllc.com/blog/</link>
    <atom:link href="https://mitmllc.com/blog/feed.xml" rel="self" type="application/rss+xml" />
    <description>Notes from MITM LLC on product launches, technical decisions, and lessons learned building indie Mac software.</description>
    <language>en-us</language>
    <lastBuildDate>Thu, 11 Jun 2026 09:29:15 GMT</lastBuildDate>
    <item>
      <title>Apple changed their mind</title>
      <link>https://mitmllc.com/blog/apple-changed-their-mind/</link>
      <guid isPermaLink="true">https://mitmllc.com/blog/apple-changed-their-mind/</guid>
      <pubDate>Tue, 09 Jun 2026 00:00:00 GMT</pubDate>
      <description>Apple reversed the 2.4.5 rejection of WhisperPad. The full version is back on the Mac App Store.</description>
      <author>noreply@mitmllc.com (Rene Zelaya)</author>
      <content:encoded><![CDATA[<p>It was Friday night. I was out with classmates after class, drinks at a bar, the kind of after-week wind-down where you&#39;re not expecting anything important to happen. My phone rang. Caller ID said Apple. I was in the middle of a busy bar and didn&#39;t want to pick up, and honestly part of me was skeptical too. Apple calling me directly seemed like the kind of thing a scam might pretend to be.</p>
<p>I let it go to voicemail.</p>
<p>When I checked later, the auto-transcription Apple does on incoming voicemails gave me the gist. A member of Apple&#39;s App Review team said he wanted to talk about WhisperPad. He didn&#39;t say why. I tried to call back at 9:30 Barcelona time and it was already after his working hours in Pacific. I told my classmates Apple was calling me. It felt unreal. Apple is giving me a phone call. That was the framing in my head all weekend.</p>
<h2>The wait</h2>
<p>I had a long weekend ahead of me wondering what the call was about. I let myself hope it was a reversal. I&#39;d been very public about the original rejection.  Part of me was nervous Apple might be calling for a different reason. Maybe they wanted to communicate something firm in private rather than in writing.</p>
<h2>The call</h2>
<p>On Monday I was in class. They called during a break. I picked up.</p>
<p>The conversation was three or four minutes. The Apple reviewer was calm and friendly. He told me the App Review board had taken a closer look at my original rejection and decided to reverse it. WhisperPad&#39;s use of the Accessibility API was approved. I could resubmit the version they&#39;d previously rejected, using the same method, and reference our conversation in the review notes.</p>
<p>He said: &quot;Sometimes a single reviewer&#39;s call gets revisited by the board, and the decision flips.&quot; I thanked him for his time. End of call.</p>
<h2>What I felt</h2>
<p>Mostly confused, then thankful. I&#39;d been operating for two weeks on the assumption that this path was permanently closed. I&#39;d forked the app into two separate SKUs. I&#39;d stood up direct distribution with Paddle for payments and Sparkle for updates. I&#39;d written architectural decision records explaining why the fork was permanent. And now, with one phone call, all of that had changed.</p>
<p>Ultimately, I&#39;m happy Apple reached out. They approved the app. I&#39;m stoked about it. The story is closed in the best possible way.</p>
<h2>The work of un-doing the fork</h2>
<p>The reversal sounded simple on the phone. Resubmit the version you originally built. In code, it was anything but simple.</p>
<p>When I split WhisperPad into two versions in May, I&#39;d guarded the differences behind a compile flag called <code>MAS_BUILD</code>. The flag had quietly come to mean two unrelated things. One: legitimate, permanent exclusions required by the App Store sandbox, like the in-app crash reporter that reads files the sandbox blocks. Two: feature reduction to dodge the 2.4.5 rejection — the clipboard-only paste path, the disabled transcription modes, the abbreviated onboarding. With the reversal, I needed to keep the first kind and undo the second.</p>
<p>Restoring the full app meant unwinding <code>MAS_BUILD</code> guards across seven files. Core insertion logic. App startup permissions. Settings UI. The onboarding flow. The FAQ. The result panel. Most of the work was deleting code. The compliance changes had added complexity, and the reversal removed it.</p>
<p>Net result: forty-four lines fewer than before.</p>
<h2>The Sparkle dance</h2>
<p>The trickier piece was the auto-updater. Sparkle, the long-standing framework that handles updates for non-App-Store Mac apps, ships helper executables that are incompatible with the sandbox by design. The sandbox exists to keep apps from escaping into the rest of the system. Sparkle&#39;s whole job is to escape and replace the parent app. The App Store will reject any bundle that contains a non-sandboxed executable.</p>
<p>So Sparkle has been toggled in and out of the codebase four times in two weeks. Out for the compliant 1.7. Back in for the direct launch. Out again on Monday, briefly, because the moment I tried to upload the 1.8 App Store build the validation failed with the exact error from before — Sparkle&#39;s helpers were back in the bundle because Sparkle is linked as a Swift Package and embeds automatically. Restored immediately after the MAS upload cleared.</p>
<p>That dance is fragile and manual. The permanent fix is conditional framework linking or revisiting the single-target decision, both of which are on the list. For now, git is the safety net.</p>
<h2>What this means for the studio</h2>
<p>The direct-distribution channel from mitmllc.com still exists. I&#39;m keeping it. People who want to save the App Store markup can buy direct. People who want Apple&#39;s trusted purchase flow can buy from the App Store. The same product, two channels, neither one a compromise.</p>
<p>Without the original rejection, I never would have built the direct channel. Sometimes the workaround you&#39;re forced to build becomes the thing you&#39;re glad you have. I learned a lot in two weeks I wouldn&#39;t have learned in two months of straight App Store work. The infrastructure I now have for direct distribution will outlast WhisperPad. It&#39;s there for whatever I ship next under MITM LLC.</p>
<h2>What I&#39;d say to other developers</h2>
<p>Build your own distribution channel anyway. Not because it&#39;ll force Apple to reverse a rejection ...though, hey, maybe it will, who knows. Build it because it&#39;s an asset you&#39;ll want to have regardless. And triple-check your work when you do. The biggest headache I had during the launch wasn&#39;t anything Apple did. It was that some users downloaded a version with a broken auto-updater and a stale purchase link, and they couldn&#39;t get to my payment page. That cost me real customers and a lot of email apologies. Sparkle, entitlements, license-key delivery etc.</p>
<h2>Closing</h2>
<p>Overall I&#39;m thankful this saga happened. I made real connections answering emails from customers, from people reaching out about RSI in general, from other Mac developers who&#39;d hit the same wall. It was good to see those connections form and to talk with people dealing with similar issues. I learned a lot.</p>
<p>It&#39;s been an interesting two weeks. Super hectic, super fun, and all of it on top of writing a master&#39;s thesis and building my final presentation. June is going to be a busy month.</p>
<p>WhisperPad is now what it was always supposed to be, in both places it was supposed to be. The loop is closed.</p>
<p>If you want to try it, the direct version is at <a href="https://mitmllc.com/whisperpad/">mitmllc.com/whisperpad</a>. The Mac App Store version is also live with the full auto-paste functionality now. The free tier is 120 minutes a month either way. If you want to write to me here: <a href="mailto:contact@mitmllc.com">contact@mitmllc.com</a></p>
<hr>
<p><em>This post was dictated using WhisperPad.</em></p>
]]></content:encoded>
    </item>
    <item>
      <title>Apple rejected my dictation app for using the accessibility API</title>
      <link>https://mitmllc.com/blog/apple-rejected-my-dictation-app/</link>
      <guid isPermaLink="true">https://mitmllc.com/blog/apple-rejected-my-dictation-app/</guid>
      <pubDate>Wed, 27 May 2026 00:00:00 GMT</pubDate>
      <description>Apple rejected WhisperPad under Guideline 2.4.5 for using the accessibility API to paste transcribed text. After an appeal that ended in another rejection, I split it into two versions — a compliant one on the App Store, and the original auto-paste version, direct.</description>
      <author>noreply@mitmllc.com (Rene Zelaya)</author>
      <content:encoded><![CDATA[<p>I built WhisperPad because I needed it. In the fall of 2024 the joints in my fingers started to hurt when I typed. Maybe the bill came due for spending most of my life on a keyboard: a childhood of video games, then 10 years working in tech. It got worse throughout the winter, and by early 2025, I could not type for sustained stretches without triggering an unsustainable level of pain. It was a progressive injury, so there was no single dramatic moment; just a slow narrowing of how much I could do in a day.</p>
<p>That narrowing arrived at an inconvenient time. I was between jobs and trying to decide what came next, and I had landed on applying to a master&#39;s program in human-computer interaction. My biggest fear about it was not whether I could keep up mentally. It was whether my hands would let me produce the work fast enough to keep pace.</p>
<h2>What WhisperPad does (or did, anyway)</h2>
<p>WhisperPad lives in your menu bar. You press a keyboard shortcut, you talk, it transcribes what you said locally on your Mac, and it places the text into whatever field your cursor is in. Nothing is sent to a server. If you have clicked away by the time it finishes, the text is on your clipboard and you can paste it wherever you want. That is the whole app. The point was to make getting words out of my head and into the computer cost as few hand movements as possible.</p>
<figure>
  <video controls preload="metadata" playsinline poster="/blog/assets/apple-rejected-my-dictation-app/window-indicator-poster.jpg">
    <source src="/blog/assets/apple-rejected-my-dictation-app/window-indicator.mp4" type="video/mp4">
    Your browser does not support the video tag. <a href="/blog/assets/apple-rejected-my-dictation-app/window-indicator.mp4">Download the video</a>.
  </video>
<figcaption>The window indicator that appears while you&#39;re dictating.</figcaption>
</figure>

<p>In April, Apple rejected an update to my Mac dictation app, WhisperPad, under Guideline 2.4.5. Their position was that I was using the accessibility API in a way that wasn&#39;t an accessibility use. The app exists because I have a hand injury. Apple had approved earlier versions doing the same thing. This time they did not.</p>
<p>I had used Apple&#39;s built-in dictation first, and the experience was a particular kind of frustrating. The transcription was close but rarely right, and every correction meant going back in with the keyboard, deleting, retyping. I was hurting my hands to fix the tool that was supposed to be saving them. That is the specific problem I wanted to solve: not &quot;transcribe my voice,&quot; but &quot;transcribe my voice well enough that I am not constantly typing corrections.&quot;</p>
<p>I will be honest about how I approached this. I did not do much market research. There were probably good tools out there already, but most sent your audio to a server somewhere, and I wanted one that didn&#39;t. When I want something, my first question is usually &quot;can I just build it,&quot; not &quot;what can I buy.&quot;</p>
<p>The first version was rough, but I used it every day and kept improving it. I shared it with a couple of classmates and watched them fold it into their own daily workflows. Seeing other people quietly come to rely on it made me want to release it properly.</p>
<h2>The rejection</h2>
<p>Version 1.0 shipped in the winter, and it was free to download from the App Store. Next I added features early users asked for, and I decided to make it a paid app. That update was version 1.5.</p>
<p>Apple rejected it under Guideline 2.4.5. The rejection was specifically about the accessibility permission. WhisperPad uses it to inject transcribed text into other applications, and Apple&#39;s position was that this was not an approved use of that API. Earlier, approved versions of the app already did the identical thing with the identical permissions, so this was hard to swallow.</p>
<p>The rejection explanation had some fair feedback, too. I fixed those issues, but the 2.4.5 issue was the real wall. So I appealed, explaining that WhisperPad functioned as an accessibility tool, that I had a repetitive strain injury, and that the whole reason the app injected text the way it did was to spare the hands of people who, like me, could not afford the extra keystrokes. I asked Apple to look at it in that light.</p>
<p>They responded that they would take a closer look. They told me not to reply in the thread, and said they would come back with a decision. That was April 21st.</p>
<p>Then it went quiet. By May 21st I had heard nothing, so I sent a short, polite note asking about the status. I have worked in customer support. I know tickets fall through the cracks, and I wanted to nudge it back onto the pile.</p>
<p>They responded quickly, and the answer was another rejection.</p>
<h2>The decision to split</h2>
<p>That second rejection is what forced a real decision, and I want to explain, because it would have been easy to make a worse one.</p>
<p>I spent some time weighing two options: comply with Apple&#39;s restrictions and release a version that fit their rules on the App Store (sacrificing the direct paste), or release WhisperPad through direct distribution. I decided I couldn&#39;t sacrifice the reach of the App Store. So I would build a version that complied with their rules and put it there, and at the same time move ahead with direct distribution to deliver the full version I had originally set out to build.</p>
<p>Here is the thing about the constraint. Apple&#39;s guideline, as far as I can tell, is meant to govern apps injecting text into themselves for accessibility, rather than into other apps. That is my assumption, since I never got a perfectly clear explanation. (If you understand this guideline better and can correct me, reach out — I want to hear it.) Either way, an app reaching into every other app on your system is something to be careful about, and I can see how WhisperPad sits in an awkward spot relative to that line.</p>
<p>So I split WhisperPad into two versions.</p>
<p>The version on the Mac App Store does not auto-paste. It puts your transcribed text on the clipboard, and you press Command-V to place it. That takes the core flow from roughly four steps to six. That does not sound like much, and for most people it is not. The entire design goal of the app was to remove hand movements for people who need to be economical with them, so a 50% increase in steps is significant. It is a compromised version of the idea. But it is a real, useful app, and it is on the store where people with or without accessibility needs can stumble onto it. The App Store makes it discoverable.</p>
<p>The original vision, the one that pastes directly where your cursor is, I shipped myself — outside the App Store.</p>
<h2>Shipping it directly</h2>
<p>Shipping software outside the App Store means rebuilding the things the store normally does for you. There&#39;s a new trust hurdle, too. You&#39;re asking people to trust you to process their credit card on your own site.</p>
<p>For payments I went with Paddle, after some research into the options. For updates I am using Sparkle, the long-standing framework for keeping Mac apps up to date outside the store. Licensing is handled with license keys checked against a small server. None of this is exotic, it is the well-worn path for independent Mac software, but it was all new to me, and getting it right took real work. Practically, I forked the app into separate build targets: one configured for the App Store, one for direct release. I finished wiring up the direct release pipeline on May 27, the morning I started writing this.</p>
<h2>What I took from this</h2>
<p>What first looked like an obstacle turned into a constraint that pushed me to build a better-organized product. I now understand my own build configuration, my update path, and my payment flow far better than I did in March, because Apple&#39;s &quot;no&quot; forced me to learn all of it. Apple&#39;s guidelines exist for defensible reasons, even if I wish my particular app had landed on the other side of the line.</p>
<p>When a platform tells you no, you usually have more options than the moment makes it feel like. The choice is rarely &quot;comply or quit.&quot; It is often &quot;comply here, and do the fuller thing somewhere else.&quot; Shipping two versions, each one serving its channel as well as that channel allows, was not a defeat. It was just the next piece of work.</p>
<p>WhisperPad is on the Mac App Store now, with a free tier of 120 minutes a month so you can find out whether it fits how you work before paying for anything. The direct version, with full auto-paste, is available at <a href="/whisperpad">mitmllc.com/whisperpad</a>. If you want to talk to me about it, I am Rene Zelaya, and you can reach me at <a href="mailto:contact@mitmllc.com">contact@mitmllc.com</a>.</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
