Skip to content
NuxieNuxie
Esc
↑↓navigate↵open⌘Jpreview
Open Nuxie
On this page

Features & Entitlements

Check feature access, observe it in SwiftUI, and consume quota or credits.

Use feature access to determine whether a customer can use a capability in your app. Pass the Feature key from Nuxie to the SDK; products and entitlements determine who receives access.

Check access

// nuxie-doc-fragment: basic-feature-access harness=async-body
let access = try await NuxieSDK.shared.hasFeature("premium-content")
if access.allowed {
    showPremiumContent()
} else {
    showUpgradePrompt()
}

hasFeature() uses fresh cached access and requests the server when necessary. A successful query can return allowed: false; a failed query throws.

For a metered feature, pass the required amount and optional entity:

// nuxie-doc-fragment: metered-feature-access harness=async-body
let access = try await NuxieSDK.shared.hasFeature(
    "ai-generations",
    requiredBalance: 5,
    entityId: "project-456"
)

if access.allowed {
    // This is presentation state only. Consume usage before the side effect.
    showEnabledGenerateButton()
}

For shared credits, use the credit feature’s own key and the action’s cost in credit units.

Access result

FeatureAccess contains allowed, unlimited, optional balance, and type (.boolean, .metered, or .creditSystem). hasAccess is an alias for allowed; hasBalance is true for unlimited access or a positive balance.

requiredBalance defaults to 1. entityId selects an allowance assigned to an entity such as a project. A check can enable an action in the UI, but it does not reserve the units for that action.

Cache policy

// nuxie-doc-fragment: feature-access-policies harness=async-body
let cachedFirst = try await NuxieSDK.shared.hasFeature(
    "premium-content",
    policy: .cacheFirst
)

let authoritative = try await NuxieSDK.shared.hasFeature(
    "premium-content",
    policy: .remote
)

.cacheFirst is the default. .remote always queries the server. Neither policy deducts usage.

Reactive SwiftUI access

NuxieSDK.shared.features updates when the customer’s access changes:

// nuxie-doc-snippet: observable-feature-view
import SwiftUI
import Nuxie

struct PremiumView: View {
    @ObservedObject private var features = NuxieSDK.shared.features

    var body: some View {
        if features.state == .unknown {
            ProgressView()
        } else if features.isAllowed("premium-content") {
            PremiumContent()
        } else {
            UpgradePrompt()
        }
    }
}
Member Description
state .unknown while loading, .reconciling while a purchase is being synchronized, or .ready.
all Cached access indexed by Feature key.
snapshot Access and state from the same update, with identityGeneration and revision.
isAllowed(_:) Whether the feature allows access.
hasBalance(_:) Whether access is unlimited or has a positive balance.
balance(_:) The metered balance, when available.
feature(_:) The full FeatureAccess value, when available.

An empty .ready snapshot is valid. Treat .unknown as loading rather than denied access.

Consume usage

Call useFeatureAndWait() before performing an action that deducts quota or credits:

// nuxie-doc-fragment: consume-feature-and-wait harness=async-body
let result = try await NuxieSDK.shared.useFeatureAndWait(
    "ai-generations",
    amount: 1,
    entityId: "project-456"
)

guard result.success else { return }

let remaining = result.authoritativeAccess?.balance
    ?? result.usage?.remaining
proceedWithGeneration(remaining: remaining)

Proceed only when success is true. The result includes the remaining balance when available.

After a native App Store purchase without a purchase delegate, the SDK can submit the matching signed purchase with the first usage request so Nuxie grants and deducts the allowance together. This also works in .observer mode, where your app finishes the transaction.

With RevenueCat, Superwall, or a custom purchase delegate, Nuxie receives only the checkout outcome. It can complete the Journey, but it does not update Features or balances. Keep using your provider’s access checks. Provider Connectors feed Automations and server-side Journeys, without granting or maintaining Feature access. Support for signed transactions from delegated checkout and Feature access updates through Connector webhooks is planned. See Purchases.

With native StoreKit and no purchase delegate, Boolean and unlimited access can update from a verified local purchase. Quotas and credit balances require server confirmation.

Usage options

Parameter Default Description
amount 1 Units to consume, or the cumulative total when setUsage is true.
entityId nil Entity whose allowance should be charged.
setUsage false Report a cumulative total and charge only its increase.
metadata nil Additional values recorded with the usage event.

Amounts must be positive whole numbers no greater than 9,007,199,254,740,991. With setUsage: true, reporting 20 and then 25 charges 20 units and then five more. A lower total does not refund usage.

FeatureUsageResult includes success, featureId, amountUsed, optional message, optional usage, and optional authoritativeAccess. usage contains current, optional limit, and optional remaining. Do not treat a thrown error as a confirmed denial.

Retry an operation

Use consumeFeature(_:quantity:operationId:entityId:) when your app needs to retain the same operation ID across requests. It is an async throwing method returning FeatureConsumptionResult. useFeatureAndWait() does not accept an app-supplied operation ID, so use consumeFeature() for work your app may need to retry.

Save an operation ID with the action before calling the SDK. Retry for the same customer, with the same Feature, quantity, entity, and operation ID. If the signed-in customer has changed, do not retry the previous customer’s action under the new identity. Nuxie returns the saved decision instead of deducting units again.

Inspect accepted before doing the work, and record that the app has applied the accepted operation so a repeated response does not perform the action twice.

Receipt field Description
customerId, featureId Customer and feature charged.
operationId, quantity The submitted action and amount.
accepted, code Whether consumption was accepted and its result code.
balance, unlimited, active Access after the operation; balance may be absent.
occurredAtMs Optional time of the operation in milliseconds.
idempotentReplay The result was already recorded for this operation ID.

An accepted last use can leave a zero balance or active: false. Authorize that action from accepted. A timeout does not undo a server-side deduction; retry with the original operation ID.

Fire-and-forget reporting

Use useFeature() when the app does not need to wait for the usage result:

// nuxie-doc-fragment: enqueue-feature-usage harness=sync-body
NuxieSDK.shared.useFeature(
    "analytics-event",
    amount: 1,
    metadata: ["source": "settings"]
)

Errors are logged. Use useFeatureAndWait() instead when acceptance determines whether the app may perform the action.

Change notifications

Assign a NuxieDelegate to NuxieSDK.shared.delegate to receive access changes:

// nuxie-doc-snippet: feature-access-observer
import Nuxie

@MainActor
final class FeatureObserver: NuxieDelegate {
    func featureAccessDidChange(
        _ featureId: String,
        from oldValue: FeatureAccess?,
        to newValue: FeatureAccess
    ) {
        print("Feature \(featureId) allowed: \(newValue.allowed)")
    }
}

Delegate methods run on the main actor. The SDK stores delegate weakly, so retain your observer in an app-owned property for as long as it should receive changes.

See Feature gating for an upgrade flow and Purchases for checkout integration.

Last updated on

Was this page helpful?