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.

ScopeCovers
Apple Team IDEvery app signed by that team.
Bundle identifiersOnly 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

TypeValid while
TrialThe current date is before the license's validUntil — 30 days from issuance.
ProductionThe 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.