The SuperwallDelegate class provides methods that are called at various points in the Superwall lifecycle. Extend this class and override methods to implement custom logic for events like paywall presentation, subscription changes, and user actions.
Import
import { SuperwallDelegate } from "expo-superwall/compat"
Usage
Create a custom delegate by extending SuperwallDelegate and overriding the methods you want to handle:
import {
SuperwallDelegate ,
EventType ,
type SuperwallEventInfo ,
type PaywallInfo ,
type SubscriptionStatus
} from "expo-superwall/compat"
class MyDelegate extends SuperwallDelegate {
handleSuperwallEvent ( eventInfo : SuperwallEventInfo ) {
switch ( eventInfo . event . type ) {
case EventType . paywallOpen :
console . log ( "Paywall opened:" , eventInfo . event . paywallInfo ?. name )
break
case EventType . paywallClose :
console . log ( "Paywall closed" )
break
case EventType . transactionComplete :
console . log ( "Transaction completed" )
break
}
}
subscriptionStatusDidChange ( from : SubscriptionStatus , to : SubscriptionStatus ) {
console . log ( `Status changed: ${ from . status } -> ${ to . status } ` )
}
}
// Set the delegate
const delegate = new MyDelegate ()
await Superwall . shared . setDelegate ( delegate )
Delegate Methods
handleSuperwallEvent()
Called for all Superwall internal events. Use this method to track analytics or respond to SDK events.
handleSuperwallEvent ( eventInfo : SuperwallEventInfo ): void
Information about the Superwall event, including event type and associated data.
Example
class MyDelegate extends SuperwallDelegate {
handleSuperwallEvent ( eventInfo ) {
const { event , params } = eventInfo
switch ( event . type ) {
case EventType . paywallOpen :
console . log ( "Paywall:" , event . paywallInfo ?. name )
// Track in analytics
analytics . track ( "paywall_opened" , {
paywallId: event . paywallInfo ?. identifier
})
break
case EventType . transactionComplete :
console . log ( "Purchase:" , event . product ?. productIdentifier )
// Track conversion
analytics . track ( "purchase_completed" , {
productId: event . product ?. productIdentifier
})
break
case EventType . paywallDecline :
console . log ( "User declined paywall" )
break
}
}
}
Common Event Types
EventType.paywallOpen - Paywall opened
EventType.paywallClose - Paywall closed
EventType.paywallDecline - User declined paywall
EventType.paywallResponseLoadStart - Paywall response loading started
EventType.paywallResponseLoadComplete - Paywall response loaded
EventType.paywallResponseLoadFail - Paywall response failed to load
EventType.paywallWebviewLoadStart - Paywall webview loading started
EventType.paywallWebviewLoadComplete - Paywall webview loaded
EventType.paywallWebviewLoadFail - Paywall webview failed to load
EventType.paywallProductsLoadStart - Products loading started
EventType.paywallProductsLoadComplete - Products loaded
EventType.paywallProductsLoadFail - Products failed to load
EventType.transactionStart - Transaction started
EventType.transactionComplete - Transaction completed
EventType.transactionFail - Transaction failed
EventType.transactionAbandon - Transaction abandoned
EventType.transactionRestore - Transaction restored
EventType.transactionTimeout - Transaction timed out
EventType.subscriptionStart - Subscription started
EventType.freeTrialStart - Free trial started
EventType.subscriptionStatusDidChange - Subscription status changed
EventType.nonRecurringProductPurchase - Non-recurring product purchased
EventType.appOpen - App opened
EventType.appClose - App closed
EventType.appLaunch - App launched
EventType.appInstall - App installed
EventType.sessionStart - Session started
EventType.firstSeen - First time user seen
EventType.configRefresh - Config refreshed
EventType.identityAlias - User identity aliased
EventType.deviceAttributes - Device attributes set
EventType.userAttributes - User attributes set
EventType.triggerFire - Placement registered
EventType.deepLink - Deep link opened
EventType.surveyResponse - Survey response submitted
EventType.surveyClose - Survey closed
EventType.restoreStart - Restore started
EventType.restoreComplete - Restore completed
EventType.restoreFail - Restore failed
subscriptionStatusDidChange()
Called when the user’s subscription status changes.
subscriptionStatusDidChange (
from : SubscriptionStatus ,
to : SubscriptionStatus
): void
The previous subscription status.
The new subscription status.
Example
class MyDelegate extends SuperwallDelegate {
subscriptionStatusDidChange ( from , to ) {
console . log ( `Status: ${ from . status } -> ${ to . status } ` )
if ( to . status === "ACTIVE" ) {
console . log ( "User is now subscribed!" )
console . log ( "Entitlements:" , to . entitlements )
// Update app UI for premium features
updateUI ({ isPremium: true })
} else if ( to . status === "INACTIVE" ) {
console . log ( "User subscription expired" )
// Revert UI to free tier
updateUI ({ isPremium: false })
}
}
}
willPresentPaywall()
Called just before a paywall is presented.
willPresentPaywall ( paywallInfo : PaywallInfo ): void
Information about the paywall that will be presented.
Example
class MyDelegate extends SuperwallDelegate {
willPresentPaywall ( paywallInfo ) {
console . log ( "About to show paywall:" , paywallInfo . name )
// Pause music, hide UI elements, etc.
}
}
didPresentPaywall()
Called after a paywall has been presented.
didPresentPaywall ( paywallInfo : PaywallInfo ): void
Information about the paywall that was presented.
Example
class MyDelegate extends SuperwallDelegate {
didPresentPaywall ( paywallInfo ) {
console . log ( "Paywall is now visible:" , paywallInfo . name )
analytics . track ( "paywall_displayed" , {
paywallId: paywallInfo . identifier ,
paywallName: paywallInfo . name
})
}
}
willDismissPaywall()
Called just before a paywall is dismissed.
willDismissPaywall ( paywallInfo : PaywallInfo ): void
Information about the paywall that will be dismissed.
Example
class MyDelegate extends SuperwallDelegate {
willDismissPaywall ( paywallInfo ) {
console . log ( "Paywall about to close:" , paywallInfo . name )
}
}
didDismissPaywall()
Called after a paywall has been dismissed.
didDismissPaywall ( paywallInfo : PaywallInfo ): void
Information about the paywall that was dismissed.
Example
class MyDelegate extends SuperwallDelegate {
didDismissPaywall ( paywallInfo ) {
console . log ( "Paywall closed:" , paywallInfo . name )
// Resume music, show UI elements, etc.
}
}
handleCustomPaywallAction()
Called when a custom action is triggered from a paywall.
handleCustomPaywallAction ( name : string ): void
The name of the custom action triggered.
Example
class MyDelegate extends SuperwallDelegate {
handleCustomPaywallAction ( name ) {
console . log ( "Custom action:" , name )
switch ( name ) {
case "contact_support" :
// Open support chat
openSupportChat ()
break
case "view_features" :
// Navigate to features screen
navigation . navigate ( "Features" )
break
}
}
}
paywallWillOpenURL()
Called when the paywall attempts to open a URL.
paywallWillOpenURL ( url : URL ): void
The URL that the paywall will attempt to open.
Example
class MyDelegate extends SuperwallDelegate {
paywallWillOpenURL ( url ) {
console . log ( "Opening URL:" , url . toString ())
// Track URL clicks
analytics . track ( "paywall_url_clicked" , {
url: url . toString ()
})
}
}
paywallWillOpenDeepLink()
Called when the paywall attempts to open a deep link.
paywallWillOpenDeepLink ( url : URL ): void
The deep link URL that the paywall will attempt to open.
Example
class MyDelegate extends SuperwallDelegate {
paywallWillOpenDeepLink ( url ) {
console . log ( "Opening deep link:" , url . toString ())
// Handle custom deep link routing
handleDeepLink ( url )
}
}
willRedeemLink()
Called before the SDK attempts to redeem a promotional link.
Example
class MyDelegate extends SuperwallDelegate {
willRedeemLink () {
console . log ( "Starting link redemption..." )
// Show loading indicator
showLoading ()
}
}
didRedeemLink()
Called after the SDK has attempted to redeem a promotional link.
didRedeemLink ( result : RedemptionResult ): void
The result of the redemption attempt.
Example
class MyDelegate extends SuperwallDelegate {
didRedeemLink ( result ) {
console . log ( "Link redemption result:" , result )
hideLoading ()
if ( result . status === "success" ) {
showSuccessMessage ( "Code redeemed successfully!" )
} else {
showErrorMessage ( "Failed to redeem code" )
}
}
}
handleLog()
Called for logging messages from the SDK.
handleLog (
level : string ,
scope : string ,
message ?: string ,
info ?: Record < string , any > | null ,
error ?: string | null
): void
The log level (e.g., “debug”, “info”, “warn”, “error”).
The scope of the log message.
info
Record<string, any> | null
Additional info associated with the log.
Error message if applicable.
Example
class MyDelegate extends SuperwallDelegate {
handleLog ( level , scope , message , info , error ) {
// Forward to your logging service
if ( level === "error" ) {
errorLogger . log ({
scope ,
message ,
info ,
error
})
} else if ( level === "debug" && __DEV__ ) {
console . log ( `[ ${ scope } ] ${ message } ` , info )
}
}
}
Complete Example
import Superwall , {
SuperwallDelegate ,
EventType ,
type SuperwallEventInfo ,
type PaywallInfo ,
type SubscriptionStatus ,
type RedemptionResult
} from "expo-superwall/compat"
class MyAppDelegate extends SuperwallDelegate {
// Track all Superwall events
handleSuperwallEvent ( eventInfo : SuperwallEventInfo ) {
const { event } = eventInfo
switch ( event . type ) {
case EventType . paywallOpen :
console . log ( "📱 Paywall opened:" , event . paywallInfo ?. name )
analytics . track ( "paywall_viewed" , {
paywall_id: event . paywallInfo ?. identifier ,
paywall_name: event . paywallInfo ?. name
})
break
case EventType . paywallClose :
console . log ( "❌ Paywall closed" )
break
case EventType . transactionStart :
console . log ( "🛒 Purchase started:" , event . product ?. productIdentifier )
break
case EventType . transactionComplete :
console . log ( "✅ Purchase completed!" )
analytics . track ( "purchase_completed" , {
product_id: event . product ?. productIdentifier ,
transaction_id: event . transaction ?. transactionIdentifier
})
break
case EventType . transactionFail :
console . error ( "❌ Purchase failed:" , event . error )
break
case EventType . subscriptionStart :
console . log ( "🎉 New subscription!" )
break
case EventType . freeTrialStart :
console . log ( "🎁 Free trial started!" )
break
}
}
// Handle subscription status changes
subscriptionStatusDidChange (
from : SubscriptionStatus ,
to : SubscriptionStatus
) {
console . log ( `Subscription: ${ from . status } -> ${ to . status } ` )
if ( to . status === "ACTIVE" ) {
// User became a subscriber
console . log ( "User is now premium!" )
console . log ( "Entitlements:" , to . entitlements )
// Update app state
appStore . setSubscriptionStatus ( "active" )
// Show welcome message
showNotification ({
title: "Welcome to Premium!" ,
message: "You now have access to all features."
})
} else if ( to . status === "INACTIVE" && from . status === "ACTIVE" ) {
// Subscription expired
console . log ( "Subscription expired" )
appStore . setSubscriptionStatus ( "inactive" )
}
}
// Paywall lifecycle
willPresentPaywall ( paywallInfo : PaywallInfo ) {
console . log ( "Preparing to show paywall:" , paywallInfo . name )
// Pause audio, hide overlays, etc.
audioPlayer . pause ()
}
didPresentPaywall ( paywallInfo : PaywallInfo ) {
console . log ( "Paywall visible:" , paywallInfo . name )
}
willDismissPaywall ( paywallInfo : PaywallInfo ) {
console . log ( "Paywall closing:" , paywallInfo . name )
}
didDismissPaywall ( paywallInfo : PaywallInfo ) {
console . log ( "Paywall closed:" , paywallInfo . name )
// Resume audio
audioPlayer . resume ()
}
// Custom actions
handleCustomPaywallAction ( name : string ) {
console . log ( "Custom action triggered:" , name )
switch ( name ) {
case "contact_support" :
navigation . navigate ( "Support" )
break
case "view_terms" :
Linking . openURL ( "https://example.com/terms" )
break
case "restore_purchase" :
// Trigger restore flow
restorePurchases ()
break
}
}
// URL handling
paywallWillOpenURL ( url : URL ) {
console . log ( "Opening URL:" , url . toString ())
analytics . track ( "paywall_link_clicked" , {
url: url . toString ()
})
}
paywallWillOpenDeepLink ( url : URL ) {
console . log ( "Opening deep link:" , url . toString ())
// Custom deep link handling
handleDeepLink ( url . toString ())
}
// Redemption
willRedeemLink () {
console . log ( "Starting redemption..." )
showLoading ( "Redeeming code..." )
}
didRedeemLink ( result : RedemptionResult ) {
hideLoading ()
console . log ( "Redemption result:" , result )
if ( result . status === "success" ) {
showSuccessNotification ( "Code redeemed successfully!" )
} else {
showErrorNotification ( "Failed to redeem code" )
}
}
// Logging
handleLog (
level : string ,
scope : string ,
message ?: string ,
info ?: Record < string , any > | null ,
error ?: string | null
) {
// Forward to your logging service
if ( level === "error" ) {
Sentry . captureMessage ( `[Superwall][ ${ scope } ] ${ message } ` , {
level: "error" ,
extra: { info , error }
})
}
if ( __DEV__ ) {
const emoji = level === "error" ? "❌" : "ℹ️"
console . log ( ` ${ emoji } [ ${ scope } ] ${ message } ` , info )
}
}
}
// Initialize and set delegate
const initializeSuperwall = async () => {
await Superwall . configure ({
apiKey: "your_api_key"
})
const delegate = new MyAppDelegate ()
await Superwall . shared . setDelegate ( delegate )
}
Best Practices
Analytics Integration Use handleSuperwallEvent() to track important events in your analytics platform for conversion optimization.
State Management Use subscriptionStatusDidChange() to update your app’s subscription state and UI accordingly.
Error Handling Implement handleLog() to capture errors and send them to your error tracking service.
Custom Actions Use handleCustomPaywallAction() to handle custom button actions configured in your paywall.
See Also