Feature Gating

Control access to premium features with Superwall placements.

Beta

The KMP SDK is in beta and its API may change between releases.

The idea

Superwall.register lets you register a placement to access a feature that may or may not be paywalled later in time. Whether the user can access that feature without paying is a dashboard decision, not a code decision.

fun pressedWorkoutButton() {
    // Remotely decide if a paywall is shown, and whether
    // startWorkout() is a paid-only feature.
    Superwall.register(placement = "StartWorkout") {
        navigation.startWorkout()
    }
}

Given how cheap register is, we strongly recommend registering all core functionality. That is what lets you change what is gated without shipping an app update.

What happens when you register

When you register a placement:

  1. The SDK checks your campaigns for a matching audience filter.
  2. If one matches and the user is not in a holdout, the assigned paywall is presented.
  3. Once a user is assigned a paywall for an audience, they keep seeing that paywall until you remove it from the audience or reset assignments.
  4. After the paywall closes, the SDK looks at the paywall's Feature Gating value, set in the paywall editor under General → Feature Gating:
    • Non Gated: the feature closure runs when the paywall is dismissed, whether they paid or not.
    • Gated: the feature closure runs only if the user is already paying, or begins paying.
  5. If no paywall is configured for the placement, the feature runs immediately with no extra network calls.

Gating with entitlements directly

Sometimes you need to branch on subscription state rather than gate a call. Read it synchronously:

import com.superwall.sdk.kmp.models.entitlements.SubscriptionStatus

if (Superwall.subscriptionStatus.isActive) {
    showProContent()
} else {
    showFreeContent()
}

Or collect the flow to keep UI in sync. See Tracking subscription state.

Prefer register with a feature closure over hand-rolled if checks where you can. The closure keeps the decision on the dashboard; an if statement hard-codes it into the build.

Inspecting entitlements

Superwall.entitlements is an immutable snapshot:

val entitlements = Superwall.entitlements

entitlements.active     // Set<Entitlement>
entitlements.inactive   // Set<Entitlement>
entitlements.all        // Set<Entitlement>
entitlements.web        // Set<Entitlement>, granted via web checkout

Each Entitlement carries its id, productIds, store, expiry and renewal dates, and whether it is a lifetime purchase.

To resolve entitlements for specific products, use getEntitlementsByProductIds, which asks the native SDK on both platforms:

val granted = Superwall.getEntitlementsByProductIds(setOf("pro_monthly", "pro_annual"))

Previewing the outcome

To adjust UI before a placement fires (hiding an upgrade button for users who would never see a paywall, for example), ask what registering would do:

val result = Superwall.getPresentationResult(placement = "StartWorkout")

This presents nothing. It just tells you what would happen.

Next, track subscription state.

How is this guide?

On this page