Skip to Content
macOS (Swift)Sandbox and distribution

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.

MyApp.entitlements
<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 sandboxedSandboxed
KeychainThe login keychainThe login keychain, items readable by your app only
Files~/Library/Application Support and ~/Library/CachesThe same folders, inside the app’s container
Machine idIOPlatformUUID, with IOKitThe same
Computer name, OS versionRead directlyThe 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 StoreDirect, with LicenseManager
Who gets paidApple, who pays you minus its commissionYou, on your Stripe account
CustomersApple’sYours: you have their email
SandboxRequiredYour choice
UpdatesThrough the App StoreYour updater, like Sparkle
LicensesApple’sEvery 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 #endif

Apple’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.