LicenseManager
Swift licensing SDK for macOS

Sell your Mac app outside the App Store, with license keys.

Add one Swift package to your SwiftUI or AppKit app, connect Stripe, and sell from your website. Each purchase creates a license key and emails it to your customer. No backend to build, no 30% to Apple, and no cut for us.

Free up to 100 active users. No credit card required.

MyApp.swift
import LicenseManagerKit
import SwiftUI

let licenseManager = try! LicenseManager(
    productID: "x7Kp2QaZ",
    publicKey: PUBLIC_KEY
)

@main
struct MyApp: App {
    @StateObject var license = LicenseObserver(manager: licenseManager)

    var body: some Scene {
        WindowGroup {
            // .active, .missingKey, .expired…
            if license.state == .active {
                ContentView()
            } else {
                ActivationView()
            }
        }
        .environmentObject(license)
    }
}

Your customer pastes the key, your app unlocks

Outside the App Store

Why most indie Mac apps sell from their own website

The Mac App Store sells your app for you, and takes up to 30% for it. Selling directly means you need license keys, and keep everything else.

TopicMac App StoreYour website, with LicenseManager.dev
Cut of each sale30%, or 15% in the Small Business ProgramStripe's card fees, and a flat monthly price
CustomersApple's: no email, no way to reach themYours: their email comes with every purchase
Paid upgradesNot supported: a new app, or a subscriptionA year of updates, then a paid renewal
Free trialWorkarounds with in-app purchasesA trial policy: 14 days on 1 Mac, for instance
RefundsApple decidesYour money back guarantee, automatic
UpdatesAfter App ReviewWhenever you want, with Sparkle
App SandboxRequiredYour choice: the SDK works both ways

Setup

From zero to your first sale in 5 minutes

Two minutes in the dashboard, three in your code. Then share your Stripe link and start selling.

  1. 11 min

    Set up your licensing model

    Sign up, add your app, and choose what you sell: a perpetual license, a yearly one, on 3 machines, with a trial…

    New policyNamePersonalDurationMonth1 yearLifetimeMachines−+12Updatesfor 1 year after purchaseCreate policyPolicy created
  2. 21 min

    Connect Stripe

    Map your Stripe price to that offer, one-time or recurring. Each payment then creates a license and emails the key to your customer, and each renewal keeps it valid.

    SStripePolicies€49one-time pricePersonal1 year · 2 machines+1€12 / mosubscriptionProWhile paid · 5 machines+1Each payment creates a license
33 min

Unlock your app when the license is active

Add the Swift package, and create the license manager when your app starts, like above. Your app shows its content when the state is .active, and otherwise your activation screen, which passes the key your customer enters to activate(key:).

That's the whole integration. Validation, storage, offline use and renewals are done for you.

Package.swift
// Xcode: File > Add Package Dependencies…
.package(
    url: "https://github.com/licenseman/licensemanager-swift",
    from: "1.0.0"
)
ActivationView.swift
struct ActivationView: View {
    @EnvironmentObject var license: LicenseObserver
    @State var key = ""
    @State var error: String?

    var body: some View {
        TextField("License key", text: $key)
        Button("Activate") {
            Task {
                do {
                    try await license.activate(key: key)
                } catch {
                    self.error = "This license key wasn't recognized."
                }
            }
        }
    }
}

No backend to write. Not even a small one.

Stripe talks to LicenseManager, and your app talks to LicenseManager, with a public key and no secret to ship. No webhooks, database of keys or emails of yours to host and keep running at 3 a.m.

StripeYour customer pays
LicenseManagerCreates and emails the key
Your Mac appValidates the key

Your serverNot needed

Where it's checked

Checked in compiled Swift, stored in the Keychain

The usual mistakes of a homemade license check on the Mac: a isPro flag in UserDefaults anyone can flip with defaults write, a key compared with a string baked into the binary, a check that locks out customers on a plane.

The SDK keeps the key and a signed token in the Keychain, verifies the signature with CryptoKit on every launch, and only locks the app when LicenseManager says the license is over.

Your app, in SwiftCompiled, signed and notarized
  • Validates the key with LicenseManager
  • Verifies the Ed25519 signed token with CryptoKit
  • Reads the Mac's hardware UUID with IOKit
  • Publishes the state to SwiftUI
What it never trustsAnything the user can edit
  • Preferences and plist files
  • A token whose signature doesn't match
  • A system clock set back
  • A key shared past its machine limit

What it stops, and what it doesn't

  • A key shared beyond its machine limit
  • A forged key or license token
  • A clock set back to stretch a license
  • A version the license doesn't include
  • A refunded or canceled license
What no license system stops: someone patching your binary and re-signing it. It breaks your Developer ID signature, so their copy isn't notarized, and honest customers are most of your sales.

Swift SDK

The hard parts are already written

Licensing a Mac app well takes more than one request. The SDK does it the way you would, after a few months of support tickets.

Distribution

Everything it takes to sell a Mac app directly

License keys are one step. Here are the others, in order, and what the SDK needs from each: nothing special.

  1. 1

    Get a Developer ID certificate

    From the Apple Developer Program, $99 a year. Sign your app with Developer ID Application, with the hardened runtime on: the SDK needs no exception.

  2. 2

    Notarize and staple

    Submit your build with notarytool, then staple the ticket, so Gatekeeper opens your app without a warning on first launch.

  3. 3

    Ship a DMG or a zip

    Drag to Applications is what Mac users expect. Host the file anywhere: your site, a CDN or GitHub releases.

  4. 4

    Update with Sparkle

    The standard updater for Mac apps sold directly. The SDK tells you the latest version a license allows, so Sparkle only offers what it includes.

  5. 5

    Sell with a Stripe Payment Link

    Put it on your website. Each purchase creates a license key, emailed to your customer, who pastes it in your app.

After the sale

Lost keys, new laptops and refunds, without a support ticket

Selling is the easy part. Then customers lose their key, replace their computer, and ask for their money back. They handle all of it themselves.

A customer portal for every product

Customers sign in with their email to copy their key, free a machine for their new computer, cancel during your money back guarantee and get refunded, or update their card. Open it from your app's Help menu with portalURL.

licenses.mosaic.appMosaicada@example.comMosaic Pro · Personal7KQ2M-X9ZPA-H4TRC-WN6EYCopyCopiedMachines2 of 21 of 2MacBook PromacOS 15 · v2.3.1DeactivateOld iMacmacOS 12 · v2.1.0DeactivateMacBook AirmacOS 26 · v2.3.1Deactivate

See who uses which version, and what sells

Active machines by platform and version, adoption of each release, revenue by offer, MRR and churn. Built from the validations your app already makes, with no tracking code.

Revenue, last 30 days€4,820+18%Active machines1,284Revenue per policyPersonalBusinessTeamMachines by versionv2.3v2.2

Free trials, without a separate code path

A trial is a license with a trial policy: 14 days on 1 machine, for instance. Your app checks it like any other key, and it stops at the end of the trial, even offline. Upgrading moves the customer to a paid license.

“1 year of updates”, enforced

Add each version you release, with its date. A license with a year of updates then accepts every version released during that year, and keeps them forever. Each validation checks the running version, and the SDK tells you the latest one the license allows, so Sparkle only offers what the customer paid for.

Every model, no code:

  • Perpetual
  • Perpetual with limited time updates
  • Time limited
  • Subscription
  • Free trial
  • Limited number of machines
  • Editions
  • Money-back guarantee
How your app reacts

Guides

The Swift SDK, in detail

How it fits SwiftUI and AppKit, the sandbox, and an app you're rewriting from Electron or Tauri.

Compare

Licensing Swift apps: how the tools compare

Whether each tool has an SDK that runs in your Swift app, takes a cut of your sales, or handles sales tax as a merchant of record. From their public pricing pages and docs.

ToolSwift SDKFee on salesMerchant of recordPrice
LicenseManager.devlicensemanager-swiftNoneNoFree, then €29 / mo
LicenseSpringSwift SDKNoneNoFree, then $199 / mo
KeygenREST API onlyNoneNoFree, then $99 / mo
PolarREST API onlyFrom 3.4% + $0.30YesFree, then $20 / mo
Lemon SqueezyREST API onlyFrom 5% + $0.50YesFree
MoonbaseREST API onlyFrom 5% + €1YesFree
CryptolensREST API onlyNoneNoFree, then €299 / mo

Every tool, ranked for Swift: top license management tools for Swift, and the free ones. Or see every comparison.

Pricing

Free for your first 100 active users. Then €29 a month.

Every feature, no time limit, no credit card. Only licenses used in the last 90 days count, and €29 / month covers 1,000 of them. Customers pay you on your own Stripe account, and we never take a cut.

Say you sell 300 licenses at €49 this month:

Your sales
€14,700
A 5% cut
€735
LicenseManager
€29

FAQ

Questions before you start

On your website, you keep 100% of the price minus Stripe's fees, you get your customers' emails, and you can sell paid upgrades, trials and licenses for several Macs. The App Store brings discovery and Apple's checkout, for 15 to 30% of each sale. Many developers do both: LicenseManager is for the copies you sell yourself.

macOS 13 Ventura or later, with Swift 6 (Xcode 16 or later), in SwiftUI and AppKit apps. The package has no dependencies: it uses CryptoKit, the Keychain and IOKit. See installation and setup.

Yes. The SDK spawns no process and only needs outgoing connections: turn on the Outgoing Connections (Client) capability in a sandboxed app. Hardened runtime and notarization need nothing else. See distribution.

Yes, with a second build. On the Mac App Store, Apple handles the payment, so there's no license key to check: that build skips the SDK. The copies you sell from your website use LicenseManager. See the Mac App Store.

Because anyone can edit a preferences file with defaults write. The SDK keeps the key and a signed token in the Keychain, and verifies the token's signature with CryptoKit on every launch: an edited token is refused. See stored data.

No. The Swift SDK reads the keychain items and files our Electron and Tauri SDKs stored, and computes the same fingerprint: your customers stay activated, on the same machine of their license. See migrating from Electron or Tauri.

No. Stripe notifies LicenseManager of each payment, and the SDK validates keys with an endpoint that needs no API key, straight from your app. There's no secret to ship, and no server of yours in between.

The app keeps working on the signed token it stored, verified locally with your public key, until it expires. You choose how long that is: an hour, a week, or months. A network error or an outage never locks your customer out. See working offline.

No license system stops someone determined to modify your app. What LicenseManager stops is the common case: a key shared beyond its machine limit, a forged key or token, a clock set back, a version the license doesn't include. That's what protects most of your revenue.

Yes. A trial is a license following a trial policy, like 14 days on 1 machine. Your app validates it like any other key, it moves to .expired when the trial ends, even offline, and upgrading turns it into a paid license. See trial licenses.

They open the customer portal, sign in with their email, deactivate the old machine, and enter their key on the new one. The same portal shows lost keys and handles refunds during your money back guarantee, so none of it reaches your inbox. See customer portal.

Yes. You add each version you release, with its date, and a policy with a year of updates accepts every version released during that year, forever. LicenseManager doesn't host your builds: the SDK sends the running version with each validation, moves to .versionNotAllowed when the license doesn't include it, and tells you the latest version it allows, for Sparkle to offer. See releases.

You sell through your own Stripe account, so you can turn on Stripe Tax to calculate and collect sales tax and VAT at checkout. Merchants of record, like Paddle or Lemon Squeezy, aren't supported yet: they are coming. Until then, any payment provider can create licenses through the API.

Yes. Map a monthly or yearly Stripe price, and each subscription gets a license that renews with every payment, includes every update, and expires when the customer stops paying. The SDK picks up renewals on its own, and moves the app to .expired when the subscription ends. See subscriptions.

No. Import your existing keys through the API, from another license manager or your own server, and your customers keep the key they have. RabbitGUI did it with an activation on launch that nobody noticed. See migrating.

A license validated in the last 90 days. Customers who stop opening your app stop counting, so you only pay for the ones using it. Past 100, plans start at €29 / month, and we never take a percentage of your sales. See pricing.

Your payments, customers and invoices live in your own Stripe account, not ours. Your licenses, customers and machines can all be read through the API, to move them elsewhere. And installed copies of your app keep running on their stored token until it expires.

Something else? Write to us, a developer answers.

Your Mac app could be selling by tonight

Create a free account, add your product, and activate your first key from your app. Connect Stripe when you're ready to sell.