3rd Party Analytics

Forward Superwall events to your own analytics stack.

Beta

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

Superwall tracks events internally: paywalls opening, transactions completing, placements firing. You can forward all of them to your own analytics provider through SuperwallDelegate.

Forwarding events

Implement handleSuperwallEvent:

import com.superwall.sdk.kmp.SuperwallDelegate
import com.superwall.sdk.kmp.models.events.SuperwallEventInfo

class AnalyticsDelegate : SuperwallDelegate {
    override fun handleSuperwallEvent(eventInfo: SuperwallEventInfo) {
        analytics.track(
            name = eventInfo.eventType.name,
            properties = eventInfo.params.orEmpty(),
        )
    }
}

Superwall.delegate = AnalyticsDelegate()

On Android, handleSuperwallEvent has no guaranteed thread. It adds no dispatcher hop, so it runs wherever the SDK tracked the event from. Usually that is a background thread. On iOS it is always main.

Two consequences: do not touch UI from it without hopping to main yourself, and do not assume it is off the main thread either. Keep the body cheap and non-blocking. See Platform differences.

The event envelope

SuperwallEventInfo is a flat envelope. eventType identifies the event, and only the fields relevant to that event are non-null:

import com.superwall.sdk.kmp.models.events.EventType

override fun handleSuperwallEvent(eventInfo: SuperwallEventInfo) {
    when (eventInfo.eventType) {
        EventType.PAYWALL_OPEN -> {
            analytics.track("paywall_open", mapOf(
                "paywall_id" to eventInfo.paywallInfo?.identifier,
                "paywall_name" to eventInfo.paywallInfo?.name,
            ))
        }
        EventType.TRANSACTION_COMPLETE -> {
            analytics.track("purchase", mapOf(
                "product_id" to eventInfo.product?.productIdentifier,
            ))
        }
        else -> analytics.track(eventInfo.eventType.name, eventInfo.params.orEmpty())
    }
}

Commonly useful fields on the envelope:

Prop

Type

Sending your identifiers to Superwall

The reverse direction matters too, since Superwall can attribute better if it knows your analytics identifiers:

import com.superwall.sdk.kmp.models.events.IntegrationAttribute

Superwall.setIntegrationAttributes(
    mapOf(
        IntegrationAttribute.AMPLITUDE_USER_ID to amplitude.userId,
        IntegrationAttribute.MIXPANEL_DISTINCT_ID to mixpanel.distinctId,
        IntegrationAttribute.APPSFLYER_ID to appsFlyer.uid,
    ),
)

Supported providers include Adjust, Amplitude, AppsFlyer, Braze, OneSignal, Meta, Firebase, Singular, Iterable, Mixpanel, mParticle, CleverTap, Airship, Kochava, Tenjin, PostHog, Customer.io, and Appstack. Passing null for a value removes it.

IntegrationAttribute.FIREBASE_INSTALLATION_ID is iOS only. Setting it on Android is skipped and logs a warning. Every other attribute works on both platforms.

Capturing SDK logs

handleLog gives you the SDK's own log stream:

override fun handleLog(
    level: LogLevel,
    scope: LogScope,
    message: String?,
    info: Map<String, Any?>?,
    error: String?,
) {
    if (level == LogLevel.ERROR) {
        crashReporter.log("Superwall/${scope.name}: $message")
    }
}

handleLog fires for every internal log line, regardless of the configured log level, which is hundreds of calls for a single register. Filter early, keep the body cheap, and never block in it.

Controlling what Superwall collects

To limit what leaves the device, set eventTrackingBehavior:

Superwall.configure(
    apiKey = "pk_your_api_key",
    options = SuperwallOptions(
        eventTrackingBehavior = EventTrackingBehavior.SUPERWALL_ONLY,
    ),
)
ValueEffect
ALLEverything is tracked. The default.
SUPERWALL_ONLYOnly internal Superwall events; your tracking calls, trigger-fire events, and user-attribute updates are suppressed.
NONENothing is sent to Superwall's servers.

How is this guide?

On this page