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,
),
)| Value | Effect |
|---|---|
ALL | Everything is tracked. The default. |
SUPERWALL_ONLY | Only internal Superwall events; your tracking calls, trigger-fire events, and user-attribute updates are suppressed. |
NONE | Nothing is sent to Superwall's servers. |
How is this guide?