iOS SDK

Features & Entitlements

Check feature access and report usage from the SDK

Features & Entitlements

Gate app behavior based on what the user has purchased or been granted. Check feature access locally or in real time, and report usage for metered features.

How feature access works

Feature access is delivered to the SDK through three sources:

  1. Profile payload -- the primary source. On each profile fetch, the SDK receives the user's current feature access and caches it locally.
  2. Real-time entitlement checks -- for critical operations, the SDK can query the server directly.
  3. Purchase sync responses -- when a transaction is synced, the server returns updated feature access that is applied immediately.

The SDK merges these sources so your app always has the freshest available data.

Cache-first feature checks

Use hasFeature() for fast, non-blocking access checks. It reads from the local cache first and falls back to a network check only when needed:

swift
let access = try await NuxieSDK.shared.hasFeature("premium_content")
if access.allowed {
    showPremiumContent()
} else {
    showUpgradePrompt()
}

Checking metered features

For features with a balance (credits, quotas, usage limits), pass a requiredBalance:

swift
let access = try await NuxieSDK.shared.hasFeature(
    "ai_generations",
    requiredBalance: 5
)
 
if access.allowed {
    generateContent()
}

Entity-based balances

For per-entity limits (e.g., per-project quotas), pass an entityId:

swift
let access = try await NuxieSDK.shared.hasFeature(
    "api_calls",
    requiredBalance: 1,
    entityId: "project-456"
)

Instant cached lookups

Use getCachedFeature() for a synchronous, non-blocking read that never makes a network call. Returns nil if the feature is not in cache:

swift
let cachedAccess = await NuxieSDK.shared.getCachedFeature("premium_content")
if cachedAccess?.allowed == true {
    // Feature is available from cache
}

Real-time server checks

Use checkFeature() when you need authoritative server state for critical operations. This always makes a network call:

swift
let result = try await NuxieSDK.shared.checkFeature(
    "premium_content",
    requiredBalance: nil,
    entityId: nil
)

To force a fresh check and update the cache, use refreshFeature():

swift
let result = try await NuxieSDK.shared.refreshFeature("premium_content")

SwiftUI integration

The SDK provides FeatureInfo, an ObservableObject that updates automatically when feature access changes. Use it for reactive UI:

swift
struct PremiumView: View {
    @ObservedObject var features = NuxieSDK.shared.features
 
    var body: some View {
        if features.isAllowed("premium_content") {
            PremiumContent()
        } else {
            UpgradePrompt()
        }
    }
}

FeatureInfo provides these convenience methods:

MethodDescription
isAllowed(_ featureId:)Returns true if the user has access.
hasBalance(_ featureId:)Returns true if the feature has a positive balance.
balance(_ featureId:)Returns the current balance as an Int.
feature(_ featureId:)Returns the full FeatureAccess object.

Delegate notifications

If you set a NuxieDelegate on NuxieSDK.shared.delegate, it receives a callback whenever feature access changes:

swift
class AppDelegate: NuxieDelegate {
    func featureAccessDidChange(
        _ featureId: String,
        from oldValue: FeatureAccess?,
        to newValue: FeatureAccess?
    ) {
        // React to feature access changes
    }
}

Reporting usage

Fire-and-forget usage

Use useFeature() to report usage of a metered feature. It decrements the local balance immediately for instant UI feedback and queues a $feature_used event for server processing:

swift
// Consume 1 unit
NuxieSDK.shared.useFeature("ai_generations")
 
// Consume multiple units
NuxieSDK.shared.useFeature("export_credits", amount: 5)
 
// Per-entity usage
NuxieSDK.shared.useFeature("api_calls", amount: 1, entityId: "project-123")

The server processes the usage event asynchronously. The authoritative balance is reconciled on the next profile refresh.

Confirmed usage

Use useFeatureAndWait() when you need server confirmation that the usage was recorded:

swift
let result = try await NuxieSDK.shared.useFeatureAndWait(
    "ai_generations",
    amount: 1
)
 
if result.success {
    print("Remaining: \(result.usage?.remaining ?? 0)")
    proceedWithGeneration()
}

This method sends the event directly to the server, waits for the response, and updates the local balance with the authoritative value from the server.

Next steps

  • Purchases -- see how purchases grant feature access
  • Monetization -- configure products, features, and entitlements in the dashboard
  • Presenting Flows -- trigger a paywall when a feature check fails