superwall / Superwall-Flutter

Remotely configure every aspect of your paywall and double your revenue.
MIT License
12 stars 7 forks source link

BridgingCreator not initialized Crash in Superwall SDK During Paywall Display #20

Closed deogw closed 2 months ago

deogw commented 2 months ago

I am encountering a BridgingCreator not initialized crash when attempting to display a paywall using the Superwall SDK in my Flutter application. The integration is successful, and users can navigate the app, but some users experience crashes when the paywall is displayed.

Crash analytics show that over 70% of these crashes occur when the device is in the background, possibly indicating an issue with background processes or initialization. However, I have not been able to reproduce this error during testing.

Below is the detailed error log:

Fatal Exception: java.lang.IllegalStateException: BridgingCreator not initialized
       at com.superwall.superwallkit_flutter.BridgingCreator$Companion.getShared(BridgingCreator.kt:23)
       at com.superwall.superwallkit_flutter.bridges.PaywallInfoBridgeKt.createBridgeId(PaywallInfoBridge.kt:62)
       at SuperwallEventInfo_JsonKt.toJson(SuperwallEventInfo+Json.kt:63)
       at SuperwallEventInfo_JsonKt.toJson(SuperwallEventInfo+Json.kt:9)
       at com.superwall.superwallkit_flutter.bridges.SuperwallDelegateProxyBridge.handleSuperwallEvent(SuperwallDelegateProxyBridge.kt:44)
       at com.superwall.sdk.delegate.SuperwallDelegateAdapter.handleSuperwallEvent(SuperwallDelegateAdapter.kt:47)
       at com.superwall.sdk.analytics.internal.TrackingKt.track(Tracking.kt:49)
       at com.superwall.sdk.analytics.internal.TrackingKt$track$1.invokeSuspend(Tracking.kt:10)
       at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
       at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:104)
       at kotlinx.coroutines.internal.LimitedDispatcher$Worker.run(LimitedDispatcher.kt:111)
       at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:99)
       at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt)
       at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:811)
       at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:715)
       at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:685)

Environment :

Flutter Version: 3.22.2
superwallkit_flutter: ^1.2.0
purchases_flutter: ^7.0.1

Initialization Code

Below is the code snippet I am using to initialize Superwall:

Future<void> initSuperWall(BuildContext context) async {
    try {
        final purchaseController = RCPurchaseController();
        final logging = Logging();
        logging.level = LogLevel.debug;
        logging.scopes = {LogScope.all};

        final options = SuperwallOptions();

        Superwall.configure(AppConst.superwallApiKey,
            purchaseController: purchaseController,
            options: options, completion: () {
                Log.logger.d('Executing Superwall configure completion block');
        });

        Superwall.shared.setDelegate(this);
        await purchaseController.configureAndSyncSubscriptionStatus(context);
    } catch (e) {
        Log.logger.e(e);
    }
}

Additional Information

The RCPurchaseController implementation is based on the Superwall example, with a slight modification to the RevenueCat listener:

Purchases.addCustomerInfoUpdateListener((customerInfo) async {
    final purchaseApi = sl<PurchaseApi>();
    bool hasActiveEntitlementOrSubscription = customerInfo.hasActiveEntitlementOrSubscription();
    if (hasActiveEntitlementOrSubscription) {
        Superwall.shared.setSubscriptionStatus(SubscriptionStatus.active);
    } else {
        Superwall.shared.setSubscriptionStatus(SubscriptionStatus.inactive);
    }

    await purchaseApi.updatePurchaseStatus(customerInfo, context);
});

Also, here is the code related to presenting the paywall:

Future doPurchase(BuildContext context) async {
    try {
        final handler = PaywallPresentationHandler();
        final popUpProvider = sl<PopupProvider>();
        final promoManager = PromotionPrefs();
        handler
            ..onPresent((paywallInfo) async {})
            ..onDismiss((paywallInfo) async {})
            ..onError((error) {
                Log.logger.d('Handler (onError): $error');
                AppHelper.reportError(
                    error,
                    StackTrace.current,
                    reason: 'Superwall Error',
                );
            })
            ..onSkip(handleSkipReason);

        Superwall.shared.registerEvent(popUpProvider.activePromoName ?? 'regular',
            handler: handler, feature: () async {
                if (isPro) {
                    promoManager.setClosing().then((_) {
                        context.pushNamed(Routes.proStatus, extra: true);
                    });
                }
            });

        Log.logger.d('Register method called successfully.');
    } catch (e) {
        Log.logger.d('Failed to call register method: $e');
    }
}
ianrumac commented 2 months ago

Hi @deogw !

Thanks for the report and sorry for the issues - we've released an update (1.2.1) that should mitigate these crashes. If you continue seeing them after updating, please feel free to reopen the issue and let us know!

jaceknijaki commented 2 months ago

This is not fixed in v1.2.1 (that uses Android sdk v1.2.3), we can still observe 2 crashes:

Fatal Exception: java.lang.IllegalStateException: BridgingCreator not initialized
       at com.superwall.superwallkit_flutter.BridgingCreator$Companion.getShared(BridgingCreator.kt:24)
       at com.superwall.superwallkit_flutter.bridges.PaywallInfoBridgeKt.createBridgeId(PaywallInfoBridge.kt:62)
       at com.superwall.superwallkit_flutter.bridges.SuperwallDelegateProxyBridge.didDismissPaywall(SuperwallDelegateProxyBridge.kt:32)
       at com.superwall.sdk.delegate.SuperwallDelegateAdapter.didDismissPaywall(SuperwallDelegateAdapter.kt:22)
       at com.superwall.sdk.paywall.vc.PaywallView.destroyed(PaywallView.kt:320)
       at com.superwall.sdk.paywall.vc.SuperwallPaywallActivity$onStop$1.invokeSuspend(SuperwallPaywallActivity.kt:331)
       at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
       at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:104)
       at android.os.Handler.handleCallback(Handler.java:942)
       at android.os.Handler.dispatchMessage(Handler.java:99)
       at android.os.Looper.loopOnce(Looper.java:211)
       at android.os.Looper.loop(Looper.java:300)
       at android.app.ActivityThread.main(ActivityThread.java:8503)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:561)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:954)

and

Fatal Exception: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.gotiva.jigsawpuzzle/com.superwall.sdk.paywall.vc.SuperwallPaywallActivity}: kotlin.UninitializedPropertyAccessException: lateinit property instance has not been initialized
       at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3920)
       at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4073)
       at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:101)
       at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
       at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2426)
       at android.os.Handler.dispatchMessage(Handler.java:106)
       at android.os.Looper.loopOnce(Looper.java:211)
       at android.os.Looper.loop(Looper.java:300)
       at android.app.ActivityThread.main(ActivityThread.java:8503)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:561)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:954)

Caused by kotlin.UninitializedPropertyAccessException: lateinit property instance has not been initialized
       at com.superwall.sdk.Superwall$Companion.setInstance(Superwall.kt:242)
       at com.superwall.sdk.Superwall$Companion.getInstance(Superwall.kt:242)
       at com.superwall.sdk.paywall.vc.SuperwallPaywallActivity.onCreate(SuperwallPaywallActivity.kt:185)
       at android.app.Activity.performCreate(Activity.java:8589)
       at android.app.Activity.performCreate(Activity.java:8553)
       at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1445)
       at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3901)
       at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4073)
       at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:101)
       at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
       at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2426)
       at android.os.Handler.dispatchMessage(Handler.java:106)
       at android.os.Looper.loopOnce(Looper.java:211)
       at android.os.Looper.loop(Looper.java:300)
       at android.app.ActivityThread.main(ActivityThread.java:8503)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:561)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:954)

100% repro for us:

  1. Move app to the BG while paywall is visible,
  2. Try to get back to the app
  3. Crash
jaceknijaki commented 2 months ago

Above problem seems to be caused by using singleInstance launchMode. Using signleTop or singleTask works fine.

ianrumac commented 2 months ago

Seems like that was the missing piece of the puzzle to reproduce this issue, after setting it to singleInstance can reproduce it consistently - thanks for the find @jaceknijaki , I'll update our docs to explicitly mention that.

jaceknijaki commented 2 months ago

We are not able to reproduce this issue on our side any more (after switching to singleTop), but unfortunately this crash can still be seen in Google Play crash reports.

We can also see java.lang.RuntimeException caused by:

Caused by kotlin.UninitializedPropertyAccessException: lateinit property instance has not been initialized
  at com.superwall.sdk.Superwall$Companion.setInstance (Superwall.kt)
  at com.superwall.sdk.Superwall$Companion.getInstance (Superwall.kt)
  at com.superwall.sdk.paywall.vc.SuperwallPaywallActivity.onCreate (SuperwallPaywallActivity.kt)
  at android.app.Activity.performCreate (Activity.java:8338)
  at android.app.Activity.performCreate (Activity.java:8317)
  at android.app.Instrumentation.callActivityOnCreate (Instrumentation.java:1384)
  at android.app.ActivityThread.performLaunchActivity (ActivityThread.java:3750)

Currently Superwall produces ~50% of all crashes we can see on Google Play, which is serious...