Swift Markdown Kit · DOCS
Licensing
Licensing
What a license does, and does not, do
It does not gate features. Every capability renders identically with or without
one. An unlicensed build carries a small Unlicensed · macdeer.com badge
pinned to the corner of the rendered content and prints one line to the console
saying why. That is the whole enforcement mechanism.
This is deliberate. You can integrate the SDK, render your real content, profile it, and run it through CI before paying — and if a license ever fails to resolve in production, your app keeps working.
Scope
A license is a signed file, validated on-device with Ed25519. It never contacts a server.
| Scope | Covers |
|---|---|
| Apple Team ID | Every app signed by that team. |
| Bundle identifiers | Only the listed apps. |
A license may carry both, in which case both must match. Most customers want the Team ID scope: one file, every app you ship, no re-issue when you add a target.
Buying issues a Team ID license, which you bind once from your account. The binding is permanent — a signed file cannot be recalled — so check the Team ID before confirming. Bundle-scoped licenses are issued on request.
Trial and production
| Type | Valid while |
|---|---|
| Trial | The current date is before the license's validUntil — 30 days from issuance. |
| Production | The SDK build's release date is on or before the license's updatesUntil. |
The production rule is the one that matters, and it is worth being precise about: a production license never expires by date and never checks the device clock. It fixes which SDK builds you may run, by comparing the build's own release timestamp against your update window.
The consequences are the ones you want:
- Apps you have already shipped keep working forever. The window ending does not reach out and stop them.
- Users with a wrong device clock are unaffected, because the clock is not read.
- Renewal buys the right to upgrade, not the right to keep running.
Renewing means receiving a new license file with a later updatesUntil. Drop it
into the app in place of the old one; nothing else changes.
On the Simulator
The Simulator has no provisioning profile and no team-prefixed keychain, so the Apple Team ID cannot be read there. A Team ID-scoped license therefore shows the badge on the Simulator — this is expected, the console says so explicitly, and device, TestFlight and App Store builds are unaffected.
If you want a badge-free Simulator too, ask us for a bundle-scoped license alongside your team one.
Validation is otherwise identical everywhere. There is no Debug exemption, no Simulator exemption, and no environment in which the check behaves differently — which is why what you see while developing is what you get when you ship.
Diagnosing a badge
One line answers almost every question:
print(MarkdownRenderKit.licenseDiagnostics())
It reports which key verified the signature, which issuer environment signed it,
where the Team ID was read from (or why it could not be), which bundle
identifiers the license covers against the one you are running, and the SDK's
release date against your updatesUntil.
To check programmatically — in a debug assertion, say:
let status = renderer.licenseStatus
if !status.isLicensed {
print("unlicensed:", status.diagnostic?.code ?? "unknown")
}
diagnostic is nil exactly when the license resolved. Every code is listed in
the Reference.
The two most common causes
licenseTeamIdentifierUnavailable — you are on the Simulator with a Team
ID-scoped license. Expected; see above.
licenseUnknownSigningKey — the license was signed by a key this build does
not trust. In practice this is a trial license issued from our test environment
paired with a production SDK build, or the reverse. Ask us to re-issue against
the right environment.
Getting a license
Trial licenses are free and issued on request. Buying grants a production license for your Team ID, plus twelve months of updates.
- Request a trial
- Pricing and renewal
- Existing licenses are re-downloadable from your account at any time — losing the file is not a problem.