Build1 publisher3 min readPublished
Signing and licensing took as long as the code in a two-day Mac menu bar app
A menu bar copy of the iPhone Duo status ring went on sale two days after Apple showed it, and the day that was not spent writing Swift went to certificates, license keys, self-update and one permission prompt the app declines to trigger.
The Engineer · Build desk

What happened
- The developer says writing the Swift took about a day, and signing, notarization, payments, license keys, updates and the installer took the second day, with the app on sale by Friday.
- Licensing goes through Polar's customer-portal endpoints, and the app deletes a stored license only on an explicit rejection, so an API outage does not lock a paying customer out.
- Cut, Copy, Paste, Select All and Undo are menu items, so the accessory app needed a hand-built main menu, invisible to users, before the license field accepted a paste.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- decision The Location gate is why one feature is missing: showing the network name would have triggered a first-launch permission prompt, so the network name is not in the product.
- cost The Keychain problem is a release-process problem. Each new signing identity means a launch-time SecurityAgent prompt, on a product whose secret is already sitting in the buyer's inbox.
- exposure With fail-open validation, the seller carries the risk: an outage cannot lock out a paying customer, and a rejected key keeps working until the API answers.
- constraint Vibrancy cannot be regression-tested in a headless pipeline, because the offscreen render always comes back opaque, so that class of visual bug needs a human looking at a real screen.
The panel's blur died from one line of layer code. Setting `effect.layer?.cornerRadius = 13` with `masksToBounds = true` rounded the corners and left a flat grey rectangle, and no API reported an error [10]. Behind-window blending does not survive being composited into a masked layer, so the fix is to let the effect view mask itself with `maskImage`, keeping `material = .menu`, `blendingMode = .behindWindow` and `state = .active` [11]. The reason this ate time is the verification path: offscreen renders taken with `bitmapImageRepForCachingDisplay` always come out opaque, so a headless screenshot cannot show the bug at all [12].
Paste failing in the license key field has the same shape. Cut, Copy, Paste, Select All and Undo live in the menu bar as items with key equivalents; the text field itself does not implement them [14]. An `LSUIElement` app has no menu bar, so there are no items, and the key equivalents never fire [13]. You build an `NSMenu`, assign it to `NSApp.mainMenu`, and the user never sees it [14].
Storing the license record in the Keychain is the recommended practice, and it collided with re-signing: Keychain items are bound to the signing identity, so every build signed with a different identity hung launch on a `SecurityAgent` prompt asking whether this new app may read its own item [15]. The record now sits in `UserDefaults(suiteName: "app.dynamicring.mac")` [16]. This is a $4.99 utility whose secret is a license key already in the customer's email [17]. "The Keychain wasn't buying real protection here, only launch hangs," the developer wrote [18].
There is no server and no account system [20]. Polar sells the app, emails the key, and exposes customer-portal activate, validate and deactivate endpoints that need no API key in the client [20]. The app posts the key plus an organization ID, stores the returned activation ID, and revalidates at launch [21]. The rule is simple: delete a stored license only on an explicit rejection, so a downed network or a 500 leaves the customer working [22]. The sandbox switch is compiled into debug builds only, which means no user default can point a shipping copy at test billing [23]. The developer found that out by activating a sandbox key in a release build and spending ten minutes on "the key isn't recognised" [24].
The permission choice was made the same way, at design time. `CWInterface.setPower(_:)` needs no permission; the moment the app calls `scanForNetworks` or reads the current network name, macOS wants Location access [7]. So the ring reports Wi-Fi state and skips the network name [9]. A "Location prompt on first launch looks like spyware", the developer wrote of a menu bar utility [8].
Two days is one developer's workload, and about one of those days went to distribution [26]. It transfers if your app is three local system reads with change listeners [6], if it has no server-side state to keep [20], and if you already hold a Developer ID certificate. That last condition is the awkward one: only the Account Holder can create a Developer ID certificate, and Admin is not enough [25]. The post's text ends mid-sentence at that point [27].
What to watch
- Whether Polar keeps the customer-portal activate/validate/deactivate endpoints usable with no API key in the client.
- Whether storing the license in UserDefaults draws key sharing that the Keychain would have slowed.
- Whether a future macOS release extends the Location gate to more CoreWLAN calls, including power state.