Migrating from Electron or Tauri
Rewriting your Mac app in Swift doesn’t cost your customers their activation. The macOS SDK reads what the Electron SDK and the Tauri SDK stored, and computes the same fingerprint:
- the key is found: the customer isn’t asked for it again,
- the fingerprint is the same: the Mac stays the same machine of the license, and doesn’t take a new place,
- the stored token still works: the app is
.activeoffline right away.
Keep the same product: same product id, same public key.
Keep the same Keychain service
The Keychain items are found under the service they were written with:
| Previous SDK | Service |
|---|---|
| Electron SDK 1.2 and later | The app’s bundle id, or the keychainService option you passed |
| Electron SDK 1.1, Tauri SDK | dev.licensemanager.sdk |
The Swift SDK uses your app’s bundle id by default. When it’s the same as before, there’s nothing to do. Otherwise, pass the previous service:
LicenseManager.Options(keychainService: "dev.licensemanager.sdk") // moving from TauriWhen a value is only found in the files, the SDK copies it to its Keychain service: once moved, the previous service is no longer needed.
Keep the same fingerprint
If you passed a fingerprint option to the Electron or Tauri SDK, pass the same value to the Swift SDK. Otherwise, don’t pass any: the default machine id is the same IOPlatformUUID.
Keep the same version numbers
Keep numbering your versions from where the previous app was: releases and the token’s mv compare them. A Swift rewrite that restarts at 1.0 would slip under every version lock of your licenses.
What the customer sees
The first launch of the Swift build reads the Keychain items written by the previous build. They’re owned by the previous app’s code signature, so macOS asks the customer, once, to allow your new app to read them:
“Acme Photo wants to use your confidential information stored in “com.acme.photo” in your keychain.”
When the customer clicks Always Allow, it never shows again. When they deny it, the SDK reads the same values from the files, and stays activated: only for apps that aren’t sandboxed, since a sandboxed app can’t read files outside its container.
The prompt can’t be avoided: the previous SDKs created the items with the Keychain’s default access list, which only trusts the app that created them. Mention it in your release notes, so customers know it’s expected.
Mapping of the APIs
| Electron | Tauri (JS) | Swift |
|---|---|---|
new LicenseManager({ productId, publicKey }) | Builder::new(id, key) | try LicenseManager(productID:publicKey:options:) |
getState() | getState() | state |
await ready() | await ready() | await ready() |
onStateChange(fn) | watchLicense(fn) | LicenseObserver, states, onStateChange(_:) |
await setLicenseKey(key) | await setLicenseKey(key) | try await setLicenseKey(_:) |
await refresh() | await refresh() | await refresh() |
getOfflineState() | getOfflineState() | offlineState |
getLatestAllowedVersion() | getLatestAllowedVersion() | latestAllowedVersion |
getPortalUrl() | getPortalUrl() | portalURL, openPortal() |
getLicenseKey() | getLicenseKey() | licenseKey, licenseKeyHint |
await getFingerprint() | getFingerprint() | await fingerprint() |
isClockTampered() | isClockTampered() | isClockTampered |
"ACTIVE", "not_found"… | the same | .active, .notFound… with the same rawValue |
minOfflineDurationMs | min_offline_duration | minOfflineDuration (seconds) |
Edge cases
The previous app’s data was deleted, like by a cleaner app. The customer activates again, and the Mac is a new machine.
Sandboxed Swift app, prompt denied. The files of the previous app are outside the container: the customer activates again. Their Mac keeps its fingerprint only if the install id could be read.
Customers still on the previous app. Both builds can run on the same Mac, and share the same data: activating one activates the other.