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()handleSuperwallEvent is not guaranteed to arrive on any particular thread, and that may change between releases. 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?