Skip to content

License setup — iOS

You added the DVAIBridge SwiftPM (or CocoaPods) dependency. You want to ship to the App Store. Here's the licensing path.

TL;DR

Add dvai-license.jwt to your Xcode project's main bundle as a resource. At app launch, the SDK reads it from the bundle, verifies the ES256 signature offline, and unlocks production behaviour. Debug builds ignore license problems.

Where the file goes

Add the license as a bundled resource so it ships inside the .app package:

  1. Drop dvai-license.jwt into your Xcode project — drag-and-drop or File → Add Files…
  2. In the file inspector, tick Target Membership for your app target.
  3. Confirm it shows up under Build Phases → Copy Bundle Resources.

The runtime location is Bundle.main.url(forResource: "dvai-license", withExtension: "jwt"). The SDK looks there first.

Alternative locations the SDK also checks — in priority order:

  1. Inline JWT passed to start(.init(licenseToken: "...")) — useful when the token comes from your account flow at runtime.
  2. A path passed to start(.init(licenseKeyPath: ...)) — e.g. a file downloaded into Application Support.
  3. Bundle.maindvai-license.jwt.

Code: with vs. without

Default — license bundled at dvai-license.jwt:

swift
import DVAIBridge

let bound = try await DVAIBridge.shared.start(.init(
    backend: .llama,
    modelPath: modelURL.path
))
print(bound.baseUrl)             // http://127.0.0.1:38883/v1
print(bound.licenseStatus)       // .commercial(licensee: "Acme", ...)

Inline JWT — downloaded at runtime, stored in Keychain:

swift
let token = try keychain.read("dvai-license-jwt")

let bound = try await DVAIBridge.shared.start(.init(
    backend: .llama,
    modelPath: modelURL.path,
    licenseToken: token
))

Explicit file path:

swift
let bound = try await DVAIBridge.shared.start(.init(
    backend: .llama,
    modelPath: modelURL.path,
    licenseKeyPath: licensePath
))

Native license fields land in v3.3

In v3.2.x, the iOS SDK ships without licenseToken / licenseKeyPath on StartOptions. The Capacitor-wrapped path runs the JS validator automatically; pure-native Swift apps in v3.2 ship under a "dev preview" allowance. Native iOS validation arrives in v3.3 — track the milestone for the release date and pin to v3.3+ to enforce on iOS.

What happens without a license

In Release builds, start(...) throws DVAIBridgeError.licenseRequired with a verbose user-facing message:

swift
do {
    let bound = try await DVAIBridge.shared.start(...)
} catch DVAIBridgeError.licenseRequired(let reason) {
    // reason is a multi-line string suitable for a crash log.
    print(reason)
    showAlert(title: "License required", message: reason)
}

The reason names the check that failed — missing file, expired, audience mismatch — and the resolution steps.

Testing locally without a license

Debug builds skip license checks automatically. The SDK detects Debug mode via the DEBUG compile flag and the simulator's environment.

Force dev mode explicitly — e.g. for a TestFlight build you want to distribute without a real license:

swift
ProcessInfo.processInfo.environment["DVAI_FORCE_DEV"] = "1"

Rehearse the production code path inside a Debug build:

swift
setenv("DVAI_FORCE_PROD", "1", 1)

When validation fails

Error reason fragmentWhat's wrongFix
bundle resource not founddvai-license.jwt isn't in Copy Bundle ResourcesRe-add to the target
signature did not verifyToken tampered with or wrong keyRe-download from your licensor
does not authorise platform "ios"License missing "ios" in platforms claimRe-issue covering iOS
audience entries ... do not matchBundle id doesn't match aud entriesRe-issue with your CFBundleIdentifier, or use a wildcard pattern
expiredPast expRenew

The runtime audience on iOS is Bundle.main.bundleIdentifier. License templates typically ship both your bundle id — e.g. com.acme.app — and a "*" fallback for trials.

See also