Swift SDK (AurisLicensing)
AurisLicensing is the official Swift client library for the Auris Licensing module. It is built on URLSession with no external dependencies, uses async/await throughout, and supports macOS, iOS, tvOS, and watchOS. The API mirrors the C# SDK 1:1.
This package covers the Licensing module only. It does not provide IAM functionality (user authentication, OIDC, roles, or MFA). For full IAM coverage in Apple platform apps use the Auris REST API directly with a standard OAuth2 library, or reach out for integration guidance.
Swift tools version: 5.7
Platforms: macOS 12+, iOS 15+, tvOS 15+, watchOS 8+
External dependencies: none
Concurrency model: async/await, @unchecked Sendable
Installation
Add the package using Swift Package Manager. In Xcode: File → Add Package Dependencies, then enter the repository URL.
To add it manually in Package.swift:
// Package.swift
// swift-tools-version: 5.7
import PackageDescription
let package = Package(
name: "MyApp",
platforms: [
.macOS(.v12), .iOS(.v15),
],
dependencies: [
.package(url: "https://github.com/altovar-systems/auris-swift.git", from: "1.0.0"),
],
targets: [
.target(
name: "MyApp",
dependencies: [
.product(name: "AurisLicensing", package: "auris-swift"),
]
),
]
)AurisLicenseClient
AurisLicenseClient is the single entry point for all licensing operations. Create one instance per application and reuse it — it is thread-safe (@unchecked Sendable).
Initializer
import AurisLicensing
let client = AurisLicenseClient(
tenantDomain: "auth.yourdomain.com", // required — your Auris tenant domain, without https://
timeout: 10 // optional — URLSession timeout in seconds (default: 10)
)The client sets the base URL to https://{tenantDomain} and attaches the x-tenant header to every outbound request. No API key is required at the SDK level — the tenant domain identifies the tenant.
Key Validation
validate(key:)
Validates a license key against the Auris API and returns the full validation result, including the licensee, plan, capacity counters, enabled features, and entitlement metadata.
let result = try await client.validate(key: "LIC-XXXX-XXXX-XXXX")
print(result.valid) // Bool
print(result.planName) // String
print(result.licensee.id) // String
print(result.licensee.type) // String ("user" or "org")Signature:
func validate(key: String) async throws -> ValidationResultDevice Activation
activate(key:fingerprint:name:)
Activates a license key on a specific device fingerprint. The fingerprint uniquely identifies the machine (you generate it — typically a hash of hardware identifiers such as IOPlatformUUID). An optional human-readable name can be attached to the device record in the Auris console.
let device = try await client.activate(
key: "LIC-XXXX-XXXX-XXXX",
fingerprint: "sha256-of-machine-id",
name: "Alice's MacBook Pro" // optional
)
print(device.id) // String — device record ID in Auris
print(device.fingerprint) // StringSignature:
func activate(key: String, fingerprint: String, name: String? = nil) async throws -> Devicedeactivate(key:fingerprint:)
Releases a device activation, freeing the slot for reuse on another machine.
try await client.deactivate(
key: "LIC-XXXX-XXXX-XXXX",
fingerprint: "sha256-of-machine-id"
)Signature:
func deactivate(key: String, fingerprint: String) async throwsMetered Usage
recordUsage(key:metric:amount:)
Records consumption of a metered metric (for example, API calls processed, documents generated, or gigabytes stored). If amount is omitted the API increments the counter by 1.
let record = try await client.recordUsage(
key: "LIC-XXXX-XXXX-XXXX",
metric: "api_calls",
amount: 50 // optional — defaults to 1
)
print(record.metric) // String
print(record.amount) // Int64
print(record.timestamp) // DateSignature:
func recordUsage(key: String, metric: String, amount: Int64? = nil) async throws -> UsageRecordgetUsage(key:)
Retrieves all metered usage counters for a license key, keyed by metric name.
let usage = try await client.getUsage(key: "LIC-XXXX-XXXX-XXXX")
if let metric = usage["api_calls"] {
print("Used: \(metric.used) / \(metric.max ?? 0)")
print("Resets at: \(metric.resetsAt as Any)")
print("Period: \(metric.period)") // "day", "month", or "forever"
}Signature:
func getUsage(key: String) async throws -> [String: UsageMetric]Feature Flags
checkFeature(key:feature:)
Returns true if the license grants access to the named feature. Use this for gating functionality that is enabled or disabled per plan.
let hasAdvancedReporting = try await client.checkFeature(
key: "LIC-XXXX-XXXX-XXXX",
feature: "advanced_reporting"
)
guard hasAdvancedReporting else {
// Disable or hide the feature in the UI
return
}Signature:
func checkFeature(key: String, feature: String) async throws -> BoolFloating Seats
Floating seats allow a fixed number of concurrent users to share a single license. Users check out a seat when they begin work and check it back in when done. An optional lease duration limits how long a seat stays checked out without a heartbeat.
checkout(key:userId:leaseDurationMin:)
Reserves a seat for the given user. Returns the Seat record including its expiry time.
let seat = try await client.checkout(
key: "LIC-XXXX-XXXX-XXXX",
userId: "usr_alice",
leaseDurationMin: 60 // optional — lease expires after 60 minutes without heartbeat
)
print(seat.userId) // String
print(seat.expiresAt as Any) // Date?Signature:
func checkout(key: String, userId: String, leaseDurationMin: Int? = nil) async throws -> Seatcheckin(key:userId:)
Releases a previously checked-out seat, freeing it immediately for another user.
try await client.checkin(
key: "LIC-XXXX-XXXX-XXXX",
userId: "usr_alice"
)Signature:
func checkin(key: String, userId: String) async throwsheartbeat(key:userId:leaseDurationMin:)
Extends the lease on an active seat. Call this periodically (for example, every 15 minutes) from long-running sessions to prevent the seat from expiring and being reclaimed.
let seat = try await client.heartbeat(
key: "LIC-XXXX-XXXX-XXXX",
userId: "usr_alice",
leaseDurationMin: 60 // optional — reset the lease to another 60 minutes
)Signature:
func heartbeat(key: String, userId: String, leaseDurationMin: Int? = nil) async throws -> SeatOffline Licensing
Offline activation is a two-step process: the client generates a request payload, the user submits it to the Auris portal to obtain a signed JWT response, and the client validates that response locally. No network call is made during validateOfflineResponse — the JWT signature is not verified; only the exp claim is checked.
generateOfflineRequest(key:fingerprint:machineName:)
Produces a base64-encoded request payload that the user submits to the Auris console to obtain an offline license response. This method is synchronous.
let requestPayload = client.generateOfflineRequest(
key: "LIC-XXXX-XXXX-XXXX",
fingerprint: "sha256-of-machine-id",
machineName: "Alice's MacBook Pro" // optional
)
// Present requestPayload to the user. They paste it into the Auris portal
// and copy back the response string.Signature:
func generateOfflineRequest(key: String, fingerprint: String, machineName: String? = nil) -> StringvalidateOfflineResponse(_:)
Decodes and validates the JWT returned by the Auris portal. Throws AurisLicensingError.expired if the token is past its exp claim, or AurisLicensingError.parse if the token cannot be decoded.
let responseJwt = /* string obtained from the Auris portal */
do {
let result = try client.validateOfflineResponse(responseJwt)
print(result.valid)
print(result.planName)
} catch AurisLicensingError.expired {
print("Offline license has expired. Generate a new activation request.")
} catch {
print("Could not decode offline response: \(error)")
}Signature:
func validateOfflineResponse(_ responseData: String) throws -> ValidationResultmacOS / iOS Example
A complete example validating and activating a license on macOS, using IOPlatformUUID as the machine fingerprint:
import AurisLicensing
import Foundation
import IOKit
// Generate a stable machine fingerprint from the platform UUID
func machinefingerprint() -> String {
let service = IOServiceGetMatchingService(
kIOMainPortDefault,
IOServiceMatching("IOPlatformExpertDevice")
)
defer { IOObjectRelease(service) }
let uuid = IORegistryEntryCreateCFProperty(
service,
"IOPlatformUUID" as CFString,
kCFAllocatorDefault,
0
)?.takeRetainedValue() as? String ?? "unknown"
return uuid
}
@main
struct ActivationFlow {
static func main() async {
let client = AurisLicenseClient(tenantDomain: "auth.yourdomain.com")
let key = "LIC-XXXX-XXXX-XXXX"
let fingerprint = machinefingerprint()
do {
// 1. Validate the key
let validation = try await client.validate(key: key)
guard validation.valid else {
print("License key is not valid.")
return
}
print("Plan: \(validation.planName)")
// 2. Activate this device
let device = try await client.activate(
key: key,
fingerprint: fingerprint,
name: Host.current().localizedName
)
print("Activated. Device ID: \(device.id)")
// 3. Check a feature flag
let hasExport = try await client.checkFeature(key: key, feature: "pdf_export")
print("PDF export available: \(hasExport)")
} catch AurisLicensingError.api(let statusCode, let body) {
print("API error \(statusCode): \(body)")
} catch AurisLicensingError.network(let message) {
print("Network error: \(message)")
} catch {
print("Unexpected error: \(error)")
}
}
}Model Types
All model types conform to Codable and Sendable.
| Type | Properties |
|---|---|
ValidationResult | valid (Bool), planName (String), licensee (Licensee), capacity (CapacityCounter?), features ([String]), metadata ([String: AnyCodable]) |
Licensee | id (String), type (String) |
CapacityCounter | used (Int), max (Int) |
UsageMetric | used (Int64), max (Int64?), period (String), resetsAt (Date?) |
Device | id (String), fingerprint (String), name (String?), createdAt (Date) |
UsageRecord | metric (String), amount (Int64), timestamp (Date) |
Seat | userId (String), checkedOutAt (Date), expiresAt (Date?) |
AnyCodable is a type-erased wrapper for arbitrary JSON values in metadata.
Error Handling
All throwing methods (both async and synchronous) throw AurisLicensingError.
import AurisLicensing
do {
let result = try await client.validate(key: "LIC-XXXX-XXXX-XXXX")
print(result.valid)
} catch AurisLicensingError.api(let statusCode, let body) {
// Non-2xx HTTP response
print("HTTP \(statusCode): \(body)")
} catch AurisLicensingError.network(let message) {
// URLSession or connectivity failure
print("Network: \(message)")
} catch AurisLicensingError.parse(let message) {
// JSON decoding failure
print("Parse error: \(message)")
} catch AurisLicensingError.expired {
// Offline JWT has passed its exp claim
print("Offline license expired.")
}AurisLicensingError cases:
| Case | Trigger |
|---|---|
.api(statusCode: Int, body: String) | HTTP 4xx or 5xx from the Auris API |
.network(String) | URLSession connectivity failure |
.parse(String) | JSON or base64 decoding failure |
.expired | Offline JWT has passed its exp claim |
Common HTTP status codes in .api:
| statusCode | Meaning |
|---|---|
| 404 | License key not found |
| 410 | License key expired or revoked |
| 422 | Validation failed (device limit, usage cap, feature not enabled) |
Related Pages
- C# SDK — Licensing client for .NET applications
- JavaScript SDK — Full IAM SDK for browsers and Node.js
- Next.js SDK — Server-side IAM helpers for Next.js
- Licensing Guide — Key types, capacity counters, metered billing, and floating seats