Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

There is no singleton instance #1090

Open
11 tasks done
mpastewski opened this issue May 31, 2024 · 23 comments
Open
11 tasks done

There is no singleton instance #1090

mpastewski opened this issue May 31, 2024 · 23 comments
Labels
bug Something isn't working

Comments

@mpastewski
Copy link

‼️ Required data ‼️

Do not remove any of the steps from the template below. If a step is not applicable to your issue, please leave that step empty.

There are a lot of things that can contribute to things not working. Having a very basic understanding of your environment will help us understand your issue faster!

Environment

  • Output of flutter doctor
    Doctor summary (to see all details, run flutter doctor -v):
    [✓] Flutter (Channel stable, 3.19.6, on macOS 14.5 23F79 darwin-arm64, locale en-GB)
    [✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
    [✓] Xcode - develop for iOS and macOS (Xcode 15.4)
    [✓] Chrome - develop for the web
    [✓] Android Studio (version 2022.3)
    [✓] VS Code (version 1.89.1)
    [✓] Connected device (3 available)
    [✓] Network resources

• No issues found!

  • Version of purchases-flutter
    purchases_flutter: ^6.29.0
  • Testing device version e.g.: iOS 15.5, Android API 30, etc.
    Android 12-14. Might also affect older Android versions
  • How often the issue occurs- every one of your customers is impacted? Only in dev?
    Affecting 3% of users in production according to Crashlytics
  • Debug logs that reproduce the issue
    Fatal Exception: bi.g0: There is no singleton instance. Make sure you configure Purchases before trying to get the default instance. More info here: https://errors.rev.cat/configuring-sdk
  • Steps to reproduce, with a description of expected vs. actual behavior
    I am not able to reproduce it at this stage
    Other information (e.g. stacktraces, related issues, suggestions how to fix, links for us to have context, eg. stackoverflow, etc.)

Describe the bug

According to Crashlytics, 3% are affected by this issue, causing fatal crash. I encapsulated RevenueCat functionality under the class below and only using this two functions to display paywall built on RevenueCat.

  class PaywallLogic {
    static Future<Offerings> initRevenueCat(BuildContext context) async {
      await Purchases.setDebugLogsEnabled(true);
      late PurchasesConfiguration configuration;

    if (Platform.isAndroid) {
      configuration =
          PurchasesConfiguration(<<revenueCatApiKeyGoogle>>);
    } else if (Platform.isIOS) {
      configuration =
          PurchasesConfiguration(<<revenueCatApiKeyApple>>);
    }
    await Purchases.configure(configuration);
    User? user = FirebaseAuth.instance.currentUser;
    if (user != null) await Purchases.logIn(user.uid);
    Offerings offerings = await Purchases.getOfferings();
    return offerings;
  }

  static Future<void> displayPaywall(BuildContext context) async {
    if (await Purchases.isConfigured == false) {
      await initRevenueCat(context);
    }

    try {
      final FirebaseRemoteConfig remoteConfig =
          Provider.of<FlavorSettings>(context, listen: false).remoteConfig;
      final String sku = remoteConfig.getString('sku');

      Offerings offerings = await Purchases.getOfferings();
      if (offerings.getOffering(sku)!.availablePackages.isNotEmpty) {
        await RevenueCatUI.presentPaywall(
          displayCloseButton: true,
          offering: offerings.getOffering(sku)!,
        );
      }
    } on PlatformException catch (e, s) {
      (...)
      );

      print(e);
    } catch (e, s) {
      (...)
    }
  }
}

Additional context

On main screen init, I am calling (inside initState):

void initRevenueCat() async {
    if (displayPaymentScreen) {
      offering = await PaywallLogic.initRevenueCat(context);
      PaywallLogic.displayPaywall(context);
    }

    setState(() {
      showPaywall = true;
    });
  }

I always use PaywallLogic class to call RevenueCat. I tried to replace await Purchases.isConfigured == false with a custom boolean flag to track if RevenueCat has been already initiated or not, but it didn't help - I was thinking that I shouldn't use Purchases at all before configuration, however it also did not help.

Screenshot 2024-05-31 at 10 55 31

@mpastewski mpastewski added the bug Something isn't working label May 31, 2024
@RCGitBot
Copy link
Contributor

👀 We've just linked this issue to our internal tracker and notified the team. Thank you for reporting, we're checking this out!

@mshmoustafa
Copy link
Contributor

Hi @mpastewski,
Looks like the stack trace is cut off, can you see where in your code the exception is being thrown? That might give us a clue as to how the Purchases singleton is being used before it's instantiated

@mpastewski
Copy link
Author

Hi @mshmoustafa,

Thanks for the note. Unfortunately, I wasn't able to identify it... Please check the full exception message below:

Fatal Exception: bi.g0: There is no singleton instance. Make sure you configure Purchases before trying to get the default instance. More info here: https://errors.rev.cat/configuring-sdk
       at com.revenuecat.purchases.Purchases$Companion.getSharedInstance(Purchases.java:11)
       at com.revenuecat.purchases.ui.revenuecatui.data.PurchasesImpl.<init>(PurchasesImpl.java:3)
       at com.revenuecat.purchases.ui.revenuecatui.data.PaywallViewModelImpl.<init>(PaywallViewModelImpl.java:2)
       at com.revenuecat.purchases.ui.revenuecatui.data.PaywallViewModelFactory.create(PaywallViewModelFactory.java:1)
       at androidx.lifecycle.ViewModelProvider$Factory.create(ViewModelProvider.java:2)
       at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:60)
       at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:28)
       at androidx.lifecycle.viewmodel.compose.ViewModelKt.get(ViewModel.kt:42)
       at androidx.lifecycle.viewmodel.compose.ViewModelKt.viewModel(ViewModel.kt:66)
       at com.revenuecat.purchases.ui.revenuecatui.InternalPaywallKt.getPaywallViewModel(InternalPaywall.kt:126)
       at com.revenuecat.purchases.ui.revenuecatui.InternalPaywallKt.InternalPaywall(InternalPaywall.kt:102)
       at com.revenuecat.purchases.ui.revenuecatui.PaywallKt.Paywall(Paywall.kt:59)
       at com.revenuecat.purchases.ui.revenuecatui.activity.PaywallActivity$onCreate$1$1$1.invoke(PaywallActivity.java:2)
       at com.revenuecat.purchases.ui.revenuecatui.activity.PaywallActivity$onCreate$1$1$1.invoke(PaywallActivity.java:1)
       at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambdaImpl.java:49)
       at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambdaImpl.java:2)
       at androidx.compose.material3.ScaffoldKt$ScaffoldLayout$1$1$1$bodyContentPlaceables$1.invoke(Scaffold.kt:2)
       at androidx.compose.material3.ScaffoldKt$ScaffoldLayout$1$1$1$bodyContentPlaceables$1.invoke(Scaffold.kt:1)
       at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambdaImpl.java:49)
       at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambdaImpl.java:1)
       at androidx.compose.ui.layout.LayoutNodeSubcompositionsState$subcompose$3$1$1.invoke(LayoutNodeSubcompositionsState.java:2)
       at androidx.compose.ui.layout.LayoutNodeSubcompositionsState$subcompose$3$1$1.invoke(LayoutNodeSubcompositionsState.java:1)
       at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambdaImpl.java:49)
       at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambdaImpl.java:1)
       at androidx.compose.runtime.ActualJvm_jvmKt.invokeComposable(ActualJvm_jvm.kt:22)
       at androidx.compose.runtime.ComposerImpl.doCompose(ComposerImpl.java:141)
       at androidx.compose.runtime.ComposerImpl.composeContent$runtime_release(ComposerImpl.java:18)
       at androidx.compose.runtime.CompositionImpl.composeContent(CompositionImpl.java:17)
       at androidx.compose.runtime.Recomposer.composeInitial$runtime_release(Recomposer.java:34)
       at androidx.compose.runtime.ComposerImpl$CompositionContextImpl.composeInitial$runtime_release(ComposerImpl.java:16)
       at androidx.compose.runtime.CompositionImpl.setContent(CompositionImpl.java:15)
       at androidx.compose.ui.layout.LayoutNodeSubcompositionsState.subcomposeInto(LayoutNodeSubcompositionsState.java:12)
       at androidx.compose.ui.layout.LayoutNodeSubcompositionsState.subcompose(LayoutNodeSubcompositionsState.java:40)
       at androidx.compose.ui.layout.LayoutNodeSubcompositionsState.subcompose(LayoutNodeSubcompositionsState.java:58)
       at androidx.compose.ui.layout.LayoutNodeSubcompositionsState.subcompose(LayoutNodeSubcompositionsState.java:130)
       at androidx.compose.ui.layout.LayoutNodeSubcompositionsState$Scope.subcompose(LayoutNodeSubcompositionsState.java:7)
       at androidx.compose.material3.ScaffoldKt$ScaffoldLayout$1$1$1.invoke(Scaffold.kt:2)
       at androidx.compose.material3.ScaffoldKt$ScaffoldLayout$1$1$1.invoke(Scaffold.kt:1)
       at androidx.compose.ui.layout.MeasureScope$layout$1.placeChildren(MeasureScope.java:48)
       at androidx.compose.ui.layout.LayoutNodeSubcompositionsState$createMeasurePolicy$1$measure$1.placeChildren(LayoutNodeSubcompositionsState.java:9)
       at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate$layoutChildren$1$1.invoke(LayoutNodeLayoutDelegate.java:2)
       at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate$layoutChildren$1$1.invoke(LayoutNodeLayoutDelegate.java:1)
       at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.java:68)
       at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe(SnapshotStateObserver.java:60)
       at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.java:34)
       at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release(OwnerSnapshotObserver.java:17)
       at androidx.compose.ui.node.OwnerSnapshotObserver.observeLayoutSnapshotReads$ui_release(OwnerSnapshotObserver.java:23)
       at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.layoutChildren(LayoutNodeLayoutDelegate.java:94)
       at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.onNodePlaced$ui_release(LayoutNodeLayoutDelegate.java:151)
       at androidx.compose.ui.node.InnerNodeCoordinator.placeAt-f8xVGno(InnerNodeCoordinator.java:21)
       at androidx.compose.ui.layout.Placeable.access$placeAt-f8xVGno(Placeable.java)
       at androidx.compose.ui.layout.Placeable$PlacementScope.place-70tqf50(Placeable.java:32)
       at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate$placeOuterCoordinator$1.invoke(LayoutNodeLayoutDelegate.java:2)
       at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate$placeOuterCoordinator$1.invoke(LayoutNodeLayoutDelegate.java:1)
       at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.java:68)
       at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe(SnapshotStateObserver.java:60)
       at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.java:34)
       at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release(OwnerSnapshotObserver.java:17)
       at androidx.compose.ui.node.OwnerSnapshotObserver.observeLayoutModifierSnapshotReads$ui_release(OwnerSnapshotObserver.java:23)
       at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.placeOuterCoordinator-f8xVGno(LayoutNodeLayoutDelegate.java:87)
       at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.placeAt-f8xVGno(LayoutNodeLayoutDelegate.java:94)
       at androidx.compose.ui.layout.Placeable.access$placeAt-f8xVGno(Placeable.java)
       at androidx.compose.ui.layout.Placeable$PlacementScope.place-70tqf50(Placeable.java:32)
       at androidx.compose.ui.layout.Placeable$PlacementScope.place-70tqf50$default(Placeable.java:7)
       at androidx.compose.foundation.layout.BoxKt.placeInBox(Box.kt:44)
       at androidx.compose.foundation.layout.BoxKt.access$placeInBox(Box.kt)
       at androidx.compose.foundation.layout.BoxKt$boxMeasurePolicy$1$measure$2.invoke(Box.kt:2)
       at androidx.compose.foundation.layout.BoxKt$boxMeasurePolicy$1$measure$2.invoke(Box.kt:1)
       at androidx.compose.ui.layout.MeasureScope$layout$1.placeChildren(MeasureScope.java:48)
       at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate$layoutChildren$1$1.invoke(LayoutNodeLayoutDelegate.java:2)
       at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate$layoutChildren$1$1.invoke(LayoutNodeLayoutDelegate.java:1)
       at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.java:68)
       at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe(SnapshotStateObserver.java:60)
       at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.java:34)
       at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release(OwnerSnapshotObserver.java:17)
       at androidx.compose.ui.node.OwnerSnapshotObserver.observeLayoutSnapshotReads$ui_release(OwnerSnapshotObserver.java:23)
       at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.layoutChildren(LayoutNodeLayoutDelegate.java:94)
       at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.onNodePlaced$ui_release(LayoutNodeLayoutDelegate.java:151)
       at androidx.compose.ui.node.InnerNodeCoordinator.placeAt-f8xVGno(InnerNodeCoordinator.java:21)
       at androidx.compose.ui.layout.Placeable.access$placeAt-f8xVGno(Placeable.java)
       at androidx.compose.ui.layout.Placeable$PlacementScope.placeWithLayer(Placeable.java:40)
       at androidx.compose.ui.layout.Placeable$PlacementScope.placeWithLayer$default(Placeable.java:21)
       at androidx.compose.ui.graphics.SimpleGraphicsLayerModifier$measure$1.invoke(SimpleGraphicsLayerModifier.java:2)
       at androidx.compose.ui.graphics.SimpleGraphicsLayerModifier$measure$1.invoke(SimpleGraphicsLayerModifier.java:1)
       at androidx.compose.ui.layout.MeasureScope$layout$1.placeChildren(MeasureScope.java:48)
       at androidx.compose.ui.node.LayoutModifierNodeCoordinator.placeAt-f8xVGno(LayoutModifierNodeCoordinator.java:57)
       at androidx.compose.ui.layout.Placeable.access$placeAt-f8xVGno(Placeable.java)
       at androidx.compose.ui.layout.Placeable$PlacementScope.placeWithLayer-aW-9-wM(Placeable.java:36)
       at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate$placeOuterCoordinator$1.invoke(LayoutNodeLayoutDelegate.java:2)
       at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate$placeOuterCoordinator$1.invoke(LayoutNodeLayoutDelegate.java:1)
       at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.java:68)
       at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe(SnapshotStateObserver.java:60)
       at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.java:34)
       at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release(OwnerSnapshotObserver.java:17)
       at androidx.compose.ui.node.OwnerSnapshotObserver.observeLayoutModifierSnapshotReads$ui_release(OwnerSnapshotObserver.java:23)
       at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.placeOuterCoordinator-f8xVGno(LayoutNodeLayoutDelegate.java:87)
       at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.placeAt-f8xVGno(LayoutNodeLayoutDelegate.java:94)
       at androidx.compose.ui.layout.Placeable.access$placeAt-f8xVGno(Placeable.java)
       at androidx.compose.ui.layout.Placeable$PlacementScope.placeRelativeWithLayer(Placeable.java:77)
       at androidx.compose.ui.layout.Placeable$PlacementScope.placeRelativeWithLayer$default(Placeable.java:21)
       at androidx.compose.ui.layout.RootMeasurePolicy$measure$2.invoke(RootMeasurePolicy.java:2)
       at androidx.compose.ui.layout.RootMeasurePolicy$measure$2.invoke(RootMeasurePolicy.java:1)
       at androidx.compose.ui.layout.MeasureScope$layout$1.placeChildren(MeasureScope.java:48)
       at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate$layoutChildren$1$1.invoke(LayoutNodeLayoutDelegate.java:2)
       at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate$layoutChildren$1$1.invoke(LayoutNodeLayoutDelegate.java:1)
       at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.java:68)
       at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe(SnapshotStateObserver.java:60)
       at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.java:34)
       at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release(OwnerSnapshotObserver.java:17)
       at androidx.compose.ui.node.OwnerSnapshotObserver.observeLayoutSnapshotReads$ui_release(OwnerSnapshotObserver.java:23)
       at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.layoutChildren(LayoutNodeLayoutDelegate.java:94)
       at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.onNodePlaced$ui_release(LayoutNodeLayoutDelegate.java:151)
       at androidx.compose.ui.node.InnerNodeCoordinator.placeAt-f8xVGno(InnerNodeCoordinator.java:21)
       at androidx.compose.ui.layout.Placeable.access$placeAt-f8xVGno(Placeable.java)
       at androidx.compose.ui.layout.Placeable$PlacementScope.place-70tqf50(Placeable.java:32)
       at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate$placeOuterCoordinator$1.invoke(LayoutNodeLayoutDelegate.java:2)
       at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate$placeOuterCoordinator$1.invoke(LayoutNodeLayoutDelegate.java:1)
       at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.java:68)
       at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe(SnapshotStateObserver.java:60)
       at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.java:34)
       at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release(OwnerSnapshotObserver.java:17)
       at androidx.compose.ui.node.OwnerSnapshotObserver.observeLayoutModifierSnapshotReads$ui_release(OwnerSnapshotObserver.java:23)
       at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.placeOuterCoordinator-f8xVGno(LayoutNodeLayoutDelegate.java:87)
       at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.placeAt-f8xVGno(LayoutNodeLayoutDelegate.java:94)
       at androidx.compose.ui.layout.Placeable.access$placeAt-f8xVGno(Placeable.java)
       at androidx.compose.ui.layout.Placeable$PlacementScope.placeRelative(Placeable.java:73)
       at androidx.compose.ui.layout.Placeable$PlacementScope.placeRelative$default(Placeable.java:7)
       at androidx.compose.ui.node.LayoutNode.place$ui_release(LayoutNode.java:70)
       at androidx.compose.ui.node.MeasureAndLayoutDelegate.remeasureAndRelayoutIfNeeded(MeasureAndLayoutDelegate.java:123)
       at androidx.compose.ui.node.MeasureAndLayoutDelegate.access$remeasureAndRelayoutIfNeeded(MeasureAndLayoutDelegate.java)
       at androidx.compose.ui.node.MeasureAndLayoutDelegate.measureAndLayout(MeasureAndLayoutDelegate.java:73)
       at androidx.compose.ui.platform.AndroidComposeView.onLayout(AndroidComposeView.java:4)
       at android.view.View.layout(View.java:24782)
       at android.view.ViewGroup.layout(ViewGroup.java:6555)
       at androidx.compose.ui.platform.AbstractComposeView.internalOnLayout$ui_release(AbstractComposeView.java:27)
       at androidx.compose.ui.platform.AbstractComposeView.onLayout(AbstractComposeView.java)
       at android.view.View.layout(View.java:24782)
       at android.view.ViewGroup.layout(ViewGroup.java:6555)
       at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
       at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
       at android.view.View.layout(View.java:24782)
       at android.view.ViewGroup.layout(ViewGroup.java:6555)
       at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1891)
       at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1729)
       at android.widget.LinearLayout.onLayout(LinearLayout.java:1638)
       at android.view.View.layout(View.java:24782)
       at android.view.ViewGroup.layout(ViewGroup.java:6555)
       at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
       at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
       at com.android.internal.policy.DecorView.onLayout(DecorView.java:868)
       at android.view.View.layout(View.java:24782)
       at android.view.ViewGroup.layout(ViewGroup.java:6555)
       at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:4732)
       at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:3985)
       at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:2741)
       at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:10184)
       at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1552)
       at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1561)
       at android.view.Choreographer.doCallbacks(Choreographer.java:1117)
       at android.view.Choreographer.doFrame(Choreographer.java:1002)
       at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1535)
       at android.os.Handler.handleCallback(Handler.java:958)
       at android.os.Handler.dispatchMessage(Handler.java:99)
       at android.os.Looper.loopOnce(Looper.java:257)
       at android.os.Looper.loop(Looper.java:368)
       at android.app.ActivityThread.main(ActivityThread.java:8826)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:572)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1049)

@mshmoustafa
Copy link
Contributor

Thank you! Let me ask a mobile engineer for input on this.

@WesleyClarkRC
Copy link

Hi @mpastewski, Yes, you are correct that you should not try to make any calls to Purchases until you have configured the SDK. A member of our engineering team took a look at the stack trace and did confirm that it is a result of attempting to display a paywall before calling configure. Are there any other places where you potentially use Paywalls prior to the configure?

@mshmoustafa
Copy link
Contributor

Hey @mpastewski,
Since this issue hasn't had any updates recently I went ahead and closed it. But if you're still experiencing the issue, please feel free to reopen.

@SanamGoodVibesOfficial
Copy link

SanamGoodVibesOfficial commented Jul 8, 2024

This issue still persists, We had checked the isConfigured status before executing any rc methods. Please fix !!

@idish
Copy link

idish commented Sep 9, 2024

I can confirm that the issue still persists, calling and awaiting on configure() won't solve the issue, that kind of crash still happen sometimes

@nuzelac
Copy link

nuzelac commented Sep 13, 2024

I am experiencing the same. I call await Purchases.configure in main.dart and everything works fine in iOS or during testing in Android, but I see crashes in Android production related to "There is no singleton instance ..."

@lukyanov
Copy link

I am experiencing the same. I call await Purchases.configure in main.dart and everything works fine in iOS or during testing in Android, but I see crashes in Android production related to "There is no singleton instance ..."

Confirming exactly the same behaviour.

@JoonasN
Copy link

JoonasN commented Sep 26, 2024

I can confirm that I'm also experiencing the same issue.

@PrinceCraox
Copy link

+1

@danyialkhan
Copy link

Hi getting this issue almost daily logged on my crashlytics:
here is the detailed comment that I have added on the similar issue:
https://github.com/RevenueCat/purchases-flutter/issues/1154#issuecomment-2387780445

@Ankitmahadik
Copy link

I am experiencing the same. I call await Purchases.configure in main.dart and everything works fine in iOS or during testing in Android, but I see crashes in Android production related to "There is no singleton instance ..."

Facing same issue

@decimal-simplex
Copy link

I am experiencing the same. I call await Purchases.configure in main.dart and everything works fine in iOS or during testing in Android, but I see crashes in Android production related to "There is no singleton instance ..."

I can also confirm this.

@hirosz
Copy link

hirosz commented Oct 17, 2024

There is still a problem. Please open it

@Jethro87
Copy link

Jethro87 commented Oct 23, 2024

Thanks, everyone, for continuing to report this. We recently identified that this can happen on Android if:

  • the user backgrounds the app when PaywallActivity is open, and
  • enough time passes causing Android to decide to kill the app in the background, and then
  • the user resumes the app

This issue has been fixed in purchases-flutter 8.1.6. Can you please upgrade and let us know if this has been resolved?

If you continue to experience this issue after upgrading, please share breadcrumb logs and the launchMode as these will help our team dig further into this. Thanks.

@Jethro87 Jethro87 reopened this Oct 23, 2024
@Nedal1990
Copy link

Nedal1990 commented Dec 3, 2024

Hello everyone, just in case anyone looking for a solution for the above issue, I faced the same issue and I fixed it by:

  1. upgrade purchases_flutter to 8.2.2 and purchases_ui_flutter: 8.2.2
  2. I changed my initialize method code to the below

import 'dart:io' show Platform;
import 'package:firebase_auth/firebase_auth.dart';
import 'package:purchases_flutter/purchases_flutter.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:flutter/services.dart';

import '../app_constants.dart';
import '../app_state.dart';
import '../environment_values.dart';

export 'package:purchases_flutter/purchases_flutter.dart'
show Package, Offering;

Offerings? _offerings;
CustomerInfo? _customerInfo;
String? _loggedInUid;

PurchasesConfiguration? _configuration;

PurchasesConfiguration? get configuration => _configuration;
Offerings? get offerings => _offerings;
CustomerInfo? get customerInfo => _customerInfo;

set customerInfo(CustomerInfo? customerInfo) => _customerInfo = customerInfo;

Future initialize(
String appStoreKey,
String playStoreKey, {
bool debugLogEnabled = false,
bool loadDataAfterLaunch = false,
}) async {

Purchases.setLogLevel(LogLevel.info);

try {
if (Platform.isIOS) {
_configuration = PurchasesConfiguration(appStoreKey);
} else if (Platform.isAndroid) {
_configuration = PurchasesConfiguration(playStoreKey);
} else {
print("RevenueCat is not supported on this platform.");
return;
}

if(_configuration != null) {
  _configuration!.entitlementVerificationMode =
      EntitlementVerificationMode.informational;
  _configuration!.pendingTransactionsForPrepaidPlansEnabled = true;

  await Purchases.configure(_configuration!);
  await Purchases.enableAdServicesAttributionTokenCollection();

  if (loadDataAfterLaunch) {
    loadCustomerInfo();
    loadOfferings();
  } else {
    await loadCustomerInfo();
    await loadOfferings();
  }

  Purchases.addCustomerInfoUpdateListener((info) {
    customerInfo = info;
  });
}

} on Exception catch (e) {
// This should happen only in the web run mode.
print("RevenueCat initialization failed: $e");
}
}

// Purchase a package.
Future purchasePackage(String package) async {
try {
final revenueCatPackage = offerings?.current?.getPackage(package);
if (revenueCatPackage == null) {
return false;
}
customerInfo = await Purchases.purchasePackage(revenueCatPackage);
return true;
} catch (_) {
return false;
}
}

List get activeEntitlementIds => _customerInfo != null
? _customerInfo!.entitlements.active.values
.map((e) => e.identifier)
.toList()
: [];

Future loadOfferings() async {
try {
_offerings = await Purchases.getOfferings();
} on PlatformException catch (e) {
print("Error loading offerings info: $e");
}
}

Future loadCustomerInfo() async {
try {
_customerInfo = await Purchases.getCustomerInfo();
} on PlatformException catch (e) {
print("Error loading purchaser info: $e");
}
}

// Return if the user has the entitlement.
// Return null on errors.
Future<bool?> isEntitled(String entitlementId) async {
try {
customerInfo = await Purchases.getCustomerInfo();
return customerInfo!.entitlements.all[entitlementId]?.isActive ?? false;
} on Exception catch (e) {
print("Unable to check RevenueCat entitlements: $e");
return null;
}
}

// https://docs.revenuecat.com/docs/user-ids
Future login(String? uid) async {
if (kIsWeb || uid == _loggedInUid) {
return;
}
try {
if (uid != null) {
customerInfo = (await Purchases.logIn(uid)).customerInfo;
} else {
customerInfo = await Purchases.logOut();
}
_loggedInUid = uid;
} on Exception catch (e) {
print("Unable to logIn or logOut user in RevenueCat: $e");
}
}

Future restorePurchases() async {
try {
customerInfo = await Purchases.restorePurchases();
} on PlatformException catch (e) {
print("Unable to restore purchases in RevenueCat: $e");
}
}
`
3. calling it from main method with the below code:

await revenue_cat.initialize(
'appStoreKey',
'playStoreKey',
loadDataAfterLaunch: true,
);

@jakobhec
Copy link

jakobhec commented Jan 3, 2025

I am using purchases_flutter: 8.4.0 and this issue still exists on Android

@diegofrata
Copy link

diegofrata commented Jan 3, 2025

Same, we are getting a bunch of crashes that forced us to pause the rollout of our app. The very first thing we do is to configure RevenueCat, and the paywall is only invoke much later in the in the app. We never saw it happen in our tests which are mostly from cold starts.

I could try and investigate it further, but ultimately, we will just ditch RevenueCat's paywall since the team here seems unable to fix this issue. Even if the crash doesn't affect users visibly, the reports feeding into the Play Store would tank our organic reach.

@jakobhec
Copy link

jakobhec commented Jan 3, 2025

Migrating away seems to be our only option as well because this issue has existed for a long time and still hasn’t been fixed.
I am curious because you said "ditch RevenueCat's paywall". Will removing purchases_ui_flutter from our app likely be enough? Or do we also need to remove purchases_flutter?

@diegofrata
Copy link

@jakobhec all the stack traces involve the Paywall component, so in my view, removing purchases_ui_flutter is enough. If you remove purchases_flutter, then you can't use RevenueCat at all!

@jakobhec
Copy link

jakobhec commented Jan 4, 2025

@diegofrata yes that would've been annoying! Let's hope you're right

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests