{
  "id": 6888917,
  "title": "Shipping a paid Mac app outside the App Store in two days",
  "url": "https://urgent.news/2026/09/12/shipping-a-paid-mac-app-outside-the-app-store-in-two-days",
  "topic": "tech",
  "section": "Tech",
  "published": "2026-09-12T07:55:00.000Z",
  "source": {
    "name": "Dev.to",
    "slug": "dev-to",
    "url": "https://dev.to/kirk_int64/shipping-a-paid-mac-app-outside-the-app-store-in-two-days-5b4l"
  },
  "original_language": "en",
  "account": "On Wednesday, Apple unveiled the iPhone Duo and its status ring that combines battery, Wi-Fi, and volume indicators into a small ring. The author wanted this feature for their Mac app. By Friday, the application was notarized, available for purchase, and capable of self-updating. While writing the app took approximately a day, the remaining tasks such as signing, notarization, payments, license keys, updates, and the installer occupied the subsequent day. It is this latter process which will be detailed in this report, including any encountered challenges.\n\nThe application in question is a menu bar app written in Swift, utilizing AppKit for the status item and panel, SwiftUI for the panel's content, and Sparkle for updates. Its download size is 3.8 MB, and it does not rely on Electron or any external dependencies. The application reads information about battery status via IOPSCopyPowerSourcesInfo, Wi-Fi status using CoreWLAN, and volume levels with CoreAudio. Each of these system reads employs a change listener to ensure the icon refreshes only when necessary.\n\nA noteworthy aspect of the app's design is the Wi-Fi-related functionality. Unlike other system calls, accessing the Service Set Identifier (SSID) requires explicit location access permissions. To avoid a potentially intrusive location prompt on first launch, the app refrains from reading the SSID entirely. This decision ensures a seamless user experience while adhering to macOS guidelines.\n\nDesigning the application's panel, the author opted for an NSPanel with an NSVisualEffectView. To create rounded corners, the author adjusted the corner radius to 13 points and disabled masking using the layer's properties. However, when this approach failed to yield the desired result, the author modified the panel's blending mode and mask image to achieve the intended rounded appearance.\n\nOne crucial aspect of the application's functionality is its ability to update itself. The app achieves this through the use of Polar's license key service, which generates a license key upon purchase and provides an API for customers to activate and validate their licenses. This approach eliminates the need for a custom account system, as Polar handles the customer portal API. The application is designed to maintain functionality even in the absence of network connectivity or during API errors, ensuring a smooth user experience.\n\nThe application operates in LSUIElement mode, meaning it runs without a Dock icon. This mode results in the loss of certain keyboard shortcuts, such as Cmd-V (Paste), as these actions are typically handled by menu items within the application's menu bar. To compensate for this limitation, the author manually implemented the required keyboard shortcuts within a custom menu.\n\nDuring the development process, the author encountered an issue with the Keychain service, which was intended to store the license record. Upon re-signing the application with a different developer certificate, the app became unresponsive due to a SecurityAgent prompt requesting permission to access the stored license key. This issue arose because Keychain items are tied to the signing identity used during the build process. To resolve this problem, the author moved the license key to UserDefaults, a solution that allows for a more flexible licensing system without the need for Keychain restrictions.\n\nThe author also explored alternative methods for implementing licensing and payments, ultimately settling on using Polar's services. Polar offers an email-based license key distribution system and provides an API for activating, validating, and deactivating licenses. This approach simplifies the licensing process while ensuring the application can function without the need for a custom account system.\n\nIn terms of distribution, the author utilized Developer ID certificates for signing the application and notarized both the zipped app and the resulting DMG file. By notarizing both the application and the DMG, the author ensured that Gatekeeper would recognize the application as trustworthy, allowing users to execute the application without issue. This process, known as \"signing inside out,\" helps avoid potential errors and compatibility issues when distributing the application.\n\nFinally, the author employed Sparkle for application updates, ensuring that any updates to the application maintain the same level of security and functionality as the initial release. By signing the helper app and XPC services separately from the main binary, the author avoided potential issues related to entitlements carried by Sparkle's framework. This careful approach to application updates ensures a secure and reliable user experience throughout the application's lifecycle.",
  "summary": "On Wednesday Apple showed the iPhone Duo and its status ring: battery, Wi-Fi and volume folded into one small ring. I wanted it on my Mac. By Friday the app was notarized, on sale and updating itself. Writing the app took about a day. Everything else signing, notarization, payments, license keys, updates, the installer took the other day, and that's the part nobody writes about. So here it is,…",
  "key_points": [],
  "editors_take": null,
  "illustration": null,
  "coverage": {
    "outlets": 1,
    "also_reported_by": []
  },
  "ai_generated": true,
  "disclaimer": "Summaries, key points and the editor’s take are written by software from other outlets’ reporting and may contain errors — always check the linked original."
}