Using the Superwall Delegate
Observe the paywall lifecycle and SDK events from shared Kotlin code.
Beta
The KMP SDK is in beta and its API may change between releases.
SuperwallDelegate is how you observe what the SDK is doing: paywalls opening and closing, subscription status changing, events being tracked, links being redeemed.
Every method has a default no-op implementation, so override only the ones you need.
Setting the delegate
import com.superwall.sdk.kmp.Superwall
import com.superwall.sdk.kmp.SuperwallDelegate
import com.superwall.sdk.kmp.models.paywall.PaywallInfo
class MyDelegate : SuperwallDelegate {
override fun didPresentPaywall(paywallInfo: PaywallInfo) {
println("Presented ${paywallInfo.name}")
}
override fun didDismissPaywall(paywallInfo: PaywallInfo) {
println("Dismissed ${paywallInfo.name}")
}
}
Superwall.delegate = MyDelegate()delegate is one of the few members you can set before configure. The value is stored
immediately and installed into the native SDK when configuration happens, so you will not miss
early events. Setting it to null clears it.
What you can observe
Paywall lifecycle
override fun willPresentPaywall(paywallInfo: PaywallInfo) {}
override fun didPresentPaywall(paywallInfo: PaywallInfo) {}
override fun willDismissPaywall(paywallInfo: PaywallInfo) {}
override fun didDismissPaywall(paywallInfo: PaywallInfo) {}Paywall interactions
override fun handleCustomPaywallAction(name: String) {}
override fun paywallWillOpenURL(url: String) {}
override fun paywallWillOpenDeepLink(url: String) {}State changes
override fun subscriptionStatusDidChange(from: SubscriptionStatus, to: SubscriptionStatus) {}
override fun customerInfoDidChange(from: CustomerInfo, to: CustomerInfo) {}
override fun userAttributesDidChange(newAttributes: Map<String, Any?>) {}Analytics and logging
override fun handleSuperwallEvent(eventInfo: SuperwallEventInfo) {}
override fun handleLog(
level: LogLevel,
scope: LogScope,
message: String?,
info: Map<String, Any?>?,
error: String?,
) {}Web checkout redemption
override fun willRedeemLink() {}
override fun didRedeemLink(result: RedemptionResult) {}Threading
Delegate callbacks are not forced onto the main thread. They arrive on whatever thread the native SDK called from, and that is not the same on both platforms.
- Paywall lifecycle hooks arrive on the main thread on Android and iOS. Update UI from these directly.
- State-change hooks (
subscriptionStatusDidChange,customerInfoDidChange,userAttributesDidChange,willRedeemLink,didRedeemLink) arrive on a background thread on Android, and on the main thread on iOS. handleSuperwallEventandhandleLogare not guaranteed either way on Android. Neither adds a dispatcher hop, so they run on whatever thread the SDK called from.handleLogis invoked inline wherever a log statement executes, which includes the main thread. On iOS both are on main.
Two things this asks of your implementation:
- Be thread-safe. The analytics hooks are not serialized against each other.
- Be quick. They run synchronously on an SDK thread, so blocking in one slows the SDK.
If you need UI work from an analytics hook, hop yourself:
override fun subscriptionStatusDidChange(from: SubscriptionStatus, to: SubscriptionStatus) {
scope.launch(Dispatchers.Main) {
updateUi(to)
}
}Or skip the delegate for that case entirely and collect subscriptionStatusFlow from a main-dispatched scope, which keeps the threading question in one place.
Platform gap
handleSuperwallDeepLink is iOS only. superwall-android has no equivalent delegate hook, so it is never invoked on Android. See Platform differences.
// iOS only
override fun handleSuperwallDeepLink(
fullURL: String,
pathComponents: List<String>,
queryParameters: Map<String, String>,
) {}The delegate and the flows
Setting or clearing the delegate never uninstalls the SDK's own internal delegate, so subscriptionStatusFlow and customerInfoFlow keep working whether or not you have one set. Use whichever fits:
- Delegate when you want the full event firehose, or the paywall lifecycle.
- Flows when you want subscription state to drive UI, and you would rather not think about threads.
How is this guide?