Skip to main content
Migrate Already selling? Move your customers to Keylight without re-issuing a single key.
Keylight
Blog
macos swift security

Where to Store License Data on macOS

4 min read Nicolas Demanez — Founder Updated July 31, 2026

Once your Mac app has a license, it has to put it somewhere. The obvious candidates are the Keychain, UserDefaults, and a file on disk. They are not equivalent, and the wrong choice leaks license data or loses it on reinstall. Here is how to pick.

First, what you are storing

The decision is easier once you are precise about what a Mac app stores for licensing. It is usually two things:

  1. The license itself — a signed lease, or the key that resolves to one. With modern licensing this is a signed document: tamper-evident, so editing it breaks the signature.
  2. Cached validation state — the last-known-good result and when the app should next revalidate online, so the app can launch offline.

Hold on to one fact throughout: because the license is signed, no storage location can make it forgeable. A signed lease in a plain text file is still cryptographically tamper-proof. So storage is not about forgery — it is about how easily the data can be read, copied, or wiped, and how well it survives.

The options

UserDefaults is the path of least resistance — defaults is one line of code. But UserDefaults is a plain plist file in the user’s Library. Any process running as that user can read it and edit it. Storing a license there means the license is trivially copyable from one Mac to another by hand. The signature still stops forgery, but it does nothing to stop a customer copying their signed license to three friends. UserDefaults is genuinely fine for non-sensitive cached state — “next revalidation due on this date” — and a poor choice for the license itself.

A plain file you manage — JSON in Application Support, say — gives you control over format and location, and it is reasonable for the cached validation state. But an unencrypted file has the same exposure as UserDefaults: readable and copyable by the user and any process they run.

An encrypted file you manage fixes that, and it is a better option than it sounds. Application Support sits outside the .app, so the file survives updates and reinstalls exactly like a Keychain item does. Seal it with a key you derive rather than store — from the machine’s hardware identifier, plus something app-specific — and the blob is both unreadable and useless if copied to another Mac. The catch is that you have to get the cryptography right, which is the part most developers should not be writing by hand.

The Keychain is purpose-built for exactly this. It is encrypted at rest, access-controlled by the system, and it survives app reinstalls and updates, because it is not inside your app’s container. The costs are a more verbose API and one macOS-specific wrinkle: Keychain access can surface a system permission prompt to the customer, which is an unwelcome thing to put in front of someone who just paid you.

The sensible default

For the license itself, use something encrypted that lives outside the bundle. Both a device-bound encrypted file and the Keychain qualify, and either beats leaving a license in a world-readable plist. Pick between them on the prompt: the file backend never asks the customer for anything, while the Keychain buys you system-managed access control and the ability to read the item from other tools you own.

This is not a hypothetical trade-off — it is the one the Keylight SDK made. It shipped Keychain-authoritative, then moved in 0.6.0 to a device-bound AES-256-GCM file under Application Support specifically to remove the first-launch permission prompt. The Keychain is still available as an opt-in mirror or as the authoritative store for apps that want it.

For non-sensitive cached state — a “revalidate after” date, a UI flag — UserDefaults is fine and convenient. There is no need to encrypt a timestamp.

Either choice is also kinder to your customers, not just more secure. Both locations persist across reinstalls, so the “I reinstalled and lost my license” support ticket largely disappears.

You probably should not hand-roll this

Here is the practical reality: getting either option right — the correct Keychain accessibility attributes, or a key derivation that is actually bound to the device — is fiddly, and it is the kind of code that fails silently and quietly until a customer hits the edge case. Behaviour across reinstalls and migration when your storage format changes are their own small projects.

A licensing SDK handles storage for you. The KeylightSDK persists the signed lease and the validation state itself; your code never touches the storage layer directly, and switching backends is one parameter rather than a migration. You create the manager and ask for the state:

import KeylightSDK

// The SDK reads the persisted, cached lease and resolves the state.
await licensing.checkOnLaunch()

switch licensing.state {
case .licensed:     enablePaidFeatures()
case .trial(let d): showTrialBanner(daysLeft: d)
case .expired:      showRenewalPrompt()
case .invalid:      showActivationSheet()
}

If you do store licensing data yourself, the rule is simple: the license goes somewhere encrypted and outside the bundle, non-sensitive cached state can go in UserDefaults, and you do not invent your own cipher. Storage is one piece of the toolkit a shipping Mac app needs — see the best Swift packages for indie Mac apps for secure-storage options that save you from hand-rolling a Keychain wrapper, plus the rest of the stack. If you have a storage edge case worth a deeper post, send us your feedback.

Frequently asked

Where should a Mac app store its license key?+

Either the Keychain or a device-bound encrypted file under Application Support. Both are encrypted and survive reinstalls. UserDefaults and plain files are readable and editable by anyone, which is fine only for non-sensitive data.

Is it safe to store a license key in UserDefaults?+

UserDefaults is a plain plist any user or process can read and edit. A signed license stored there cannot be forged, but it can be trivially copied — an encrypted store is the better choice.

Does it matter where I store a signed license?+

Storage location does not affect forgery resistance — a signed license is tamper-evident anywhere. It affects how easily the license can be read, copied, or wiped, which is why an encrypted store is preferred.

Ready to ship?

Create your account and start licensing your apps in under a minute. Free forever tier included.

Start Free