Sandbox and distribution
The SDK works in every kind of Mac app: sandboxed or not, with the hardened runtime, notarized, distributed as a .dmg, a .zip, a .pkg, or through an updater like Sparkle . It runs no command and loads no plugin: it only needs to make HTTPS requests.
App Sandbox
A sandboxed app needs one capability: Outgoing Connections (Client), in Signing & Capabilities > App Sandbox.
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>Without it, every request fails: activations throw .networkError, and an activated app runs offline on its stored token until it expires.
Inside the sandbox:
| Not sandboxed | Sandboxed | |
|---|---|---|
| Keychain | The login keychain | The login keychain, items readable by your app only |
| Files | ~/Library/Application Support and ~/Library/Caches | The same folders, inside the app’s container |
| Machine id | IOPlatformUUID, with IOKit | The same |
| Computer name, OS version | Read directly | The same |
The fingerprint is the same whether the app is sandboxed or not: turning the sandbox on in an update doesn’t make Macs new machines, as long as the install id can be read from the Keychain. The files of a non-sandboxed build are outside the container, so a sandboxed build relies on the Keychain to find them.
Hardened runtime and notarization
Nothing to add: the SDK needs no exception of the hardened runtime, like allow-jit or disable-library-validation. Sign your app with your Developer ID Application certificate and notarize it as usual.
The Keychain ties each item to the code signature of the app that created it. Builds signed with the same Developer ID, by the same team, read their items without any prompt, update after update.
Changing your app’s bundle id, or its signing team, changes the default keychainService and the signature: the Keychain asks the customer, once, to allow access to the existing items. Keep both stable, or set keychainService to the previous bundle id. See stored data.
Development builds
Xcode’s Sign to Run Locally, and swift run, sign each build ad hoc, with a signature that changes on every build. The SDK detects it and doesn’t use the Keychain for them: no keychain prompt on every run. With an Apple Development certificate, it uses the Keychain like a release build. See development builds.
Mac App Store
On the Mac App Store, Apple sells your app: customers pay through their Apple account, and your app has no license key to check. LicenseManager is for the copies you sell yourself, from your website, with Stripe:
| Mac App Store | Direct, with LicenseManager | |
|---|---|---|
| Who gets paid | Apple, who pays you minus its commission | You, on your Stripe account |
| Customers | Apple’s | Yours: you have their email |
| Sandbox | Required | Your choice |
| Updates | Through the App Store | Your updater, like Sparkle |
| Licenses | Apple’s | Every licensing model: perpetual, yearly, per machine, with a trial… |
Many developers sell both: a Mac App Store build for discoverability, and a direct build for the customers who buy on their website, often with volume or team licenses the App Store doesn’t offer. Use a compile-time flag to leave LicenseManager out of the App Store build:
#if DIRECT_DISTRIBUTION
import LicenseManagerKit
#endifApple’s guidelines don’t allow an App Store app to ask for a license key to unlock features sold outside the App Store: keep the activation screen in the direct build only.
Updates and versions
When a new version of your app launches for the first time, its first validation sends its version: when the license doesn’t include it, the state becomes .versionNotAllowed, with the latest version the license allows. With Sparkle, compare it with the version of an update before installing it, so customers don’t install a version their license doesn’t include. See releases.