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, even if it is configured #1187

Open
9 of 11 tasks
hirosz opened this issue Oct 17, 2024 · 3 comments
Open
9 of 11 tasks

There is no singleton instance, even if it is configured #1187

hirosz opened this issue Oct 17, 2024 · 3 comments
Labels
bug Something isn't working

Comments

@hirosz
Copy link

hirosz commented Oct 17, 2024

‼️ 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
    [✓] Flutter (Channel stable, 3.24.3, on macOS 14.6.1 23G93 darwin-arm64, locale pl-PL)
    [✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
    [✓] Xcode - develop for iOS and macOS (Xcode 16.0)
    [✓] Chrome - develop for the web
    [!] Android Studio (version unknown)
    ✗ Unable to determine Android Studio version.
    [✓] VS Code (version 1.94.1)
    [✓] Connected device (5 available)
    [✓] Network resources
  • Version of purchases-flutter : 8.1.6, purchases-ui-flutter : 8.1.6
  • Testing device version e.g.: different versions of Android
  • How often the issue occurs- every one of your customers is impacted? Only in dev?
    It is in production version. Not all users
  • Debug logs that reproduce the issue
  • Steps to reproduce, with a description of expected vs. actual behavior
    Other information (e.g. stacktraces, related issues, suggestions how to fix, links for us to have context, eg. stackoverflow, etc.)

Describe the bug

RevenueCat Paywall make sometimes error:

Exception wf.f0: 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)
  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:1)
  at androidx.lifecycle.viewmodel.compose.ViewModelKt.get (ViewModel.kt:1)
  at androidx.lifecycle.viewmodel.compose.ViewModelKt.viewModel (ViewModel.kt:1)
  at com.revenuecat.purchases.ui.revenuecatui.InternalPaywallKt.getPaywallViewModel (InternalPaywall.kt:1)
  at com.revenuecat.purchases.ui.revenuecatui.InternalPaywallKt.InternalPaywall (InternalPaywall.kt:1)
  at com.revenuecat.purchases.ui.revenuecatui.PaywallKt.Paywall (Paywall.kt:1)
  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:1)
  at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke (ComposableLambdaImpl.java:2)
  at androidx.compose.material3.ScaffoldKt$ScaffoldLayoutWithMeasureFix$1$1$bodyContentPlaceables$1.invoke (Scaffold.kt:2)
  at androidx.compose.material3.ScaffoldKt$ScaffoldLayoutWithMeasureFix$1$1$bodyContentPlaceables$1.invoke (Scaffold.kt:1)
  at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke (ComposableLambdaImpl.java:1)
  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:1)
  at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke (ComposableLambdaImpl.java:1)
  at androidx.compose.runtime.ActualJvm_jvmKt.invokeComposable (ActualJvm_jvm.kt:1)
  at androidx.compose.runtime.ComposerImpl.doCompose (ComposerImpl.java:1)
  at androidx.compose.runtime.ComposerImpl.composeContent$runtime_release (ComposerImpl.java:1)
  at androidx.compose.runtime.CompositionImpl.composeContent (CompositionImpl.java:1)
  at androidx.compose.runtime.Recomposer.composeInitial$runtime_release (Recomposer.java:1)
  at androidx.compose.runtime.ComposerImpl$CompositionContextImpl.composeInitial$runtime_release (ComposerImpl.java:1)
  at androidx.compose.runtime.CompositionImpl.composeInitial (CompositionImpl.java:1)
  at androidx.compose.runtime.CompositionImpl.setContent (CompositionImpl.java:1)
  at androidx.compose.ui.layout.LayoutNodeSubcompositionsState.subcomposeInto (LayoutNodeSubcompositionsState.java:1)
  at androidx.compose.ui.layout.LayoutNodeSubcompositionsState.subcompose (LayoutNodeSubcompositionsState.java:1)
  at androidx.compose.ui.layout.LayoutNodeSubcompositionsState.subcompose (LayoutNodeSubcompositionsState.java:1)
  at androidx.compose.ui.layout.LayoutNodeSubcompositionsState.subcompose (LayoutNodeSubcompositionsState.java:1)
  at androidx.compose.ui.layout.LayoutNodeSubcompositionsState$Scope.subcompose (LayoutNodeSubcompositionsState.java:1)
  at androidx.compose.material3.ScaffoldKt$ScaffoldLayoutWithMeasureFix$1$1.invoke-0kLqBqw (Scaffold.kt:1)
  at androidx.compose.material3.ScaffoldKt$ScaffoldLayoutWithMeasureFix$1$1.invoke (Scaffold.kt:1)
  at androidx.compose.ui.layout.LayoutNodeSubcompositionsState$createMeasurePolicy$1.measure-3p2s80s (LayoutNodeSubcompositionsState.java:1)
  at androidx.compose.ui.node.InnerNodeCoordinator.measure-BRTryo0 (InnerNodeCoordinator.java:1)
  at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke (LayoutNodeLayoutDelegate.java:2)
  at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke (LayoutNodeLayoutDelegate.java:1)
  at androidx.compose.runtime.snapshots.Snapshot$Companion.observe (Snapshot.java:1)
  at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe (SnapshotStateObserver.java:1)
  at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads (SnapshotStateObserver.java:1)
  at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release (OwnerSnapshotObserver.java:1)
  at androidx.compose.ui.node.OwnerSnapshotObserver.observeMeasureSnapshotReads$ui_release (OwnerSnapshotObserver.java:1)
  at androidx.compose.ui.node.LayoutNodeLayoutDelegate.performMeasure-BRTryo0 (LayoutNodeLayoutDelegate.java:1)
  at androidx.compose.ui.node.LayoutNodeLayoutDelegate.access$performMeasure-BRTryo0 (LayoutNodeLayoutDelegate.java:1)
  at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.remeasure-BRTryo0 (LayoutNodeLayoutDelegate.java:1)
  at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.measure-BRTryo0 (LayoutNodeLayoutDelegate.java:1)
  at androidx.compose.foundation.layout.BoxMeasurePolicy.measure-3p2s80s (BoxMeasurePolicy.java:1)
  at androidx.compose.ui.node.InnerNodeCoordinator.measure-BRTryo0 (InnerNodeCoordinator.java:1)
  at androidx.compose.ui.graphics.SimpleGraphicsLayerModifier.measure-3p2s80s (SimpleGraphicsLayerModifier.java:1)
  at androidx.compose.ui.node.LayoutModifierNodeCoordinator.measure-BRTryo0 (LayoutModifierNodeCoordinator.java:1)
  at androidx.compose.ui.graphics.SimpleGraphicsLayerModifier.measure-3p2s80s (SimpleGraphicsLayerModifier.java:1)
  at androidx.compose.ui.node.LayoutModifierNodeCoordinator.measure-BRTryo0 (LayoutModifierNodeCoordinator.java:1)
  at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke (LayoutNodeLayoutDelegate.java:2)
  at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke (LayoutNodeLayoutDelegate.java:1)
  at androidx.compose.runtime.snapshots.Snapshot$Companion.observe (Snapshot.java:1)
  at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe (SnapshotStateObserver.java:1)
  at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads (SnapshotStateObserver.java:1)
  at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release (OwnerSnapshotObserver.java:1)
  at androidx.compose.ui.node.OwnerSnapshotObserver.observeMeasureSnapshotReads$ui_release (OwnerSnapshotObserver.java:1)
  at androidx.compose.ui.node.LayoutNodeLayoutDelegate.performMeasure-BRTryo0 (LayoutNodeLayoutDelegate.java:1)
  at androidx.compose.ui.node.LayoutNodeLayoutDelegate.access$performMeasure-BRTryo0 (LayoutNodeLayoutDelegate.java:1)
  at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.remeasure-BRTryo0 (LayoutNodeLayoutDelegate.java:1)
  at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.measure-BRTryo0 (LayoutNodeLayoutDelegate.java:1)
  at androidx.compose.ui.layout.RootMeasurePolicy.measure-3p2s80s (RootMeasurePolicy.java:1)
  at androidx.compose.ui.node.InnerNodeCoordinator.measure-BRTryo0 (InnerNodeCoordinator.java:1)
  at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke (LayoutNodeLayoutDelegate.java:2)
  at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke (LayoutNodeLayoutDelegate.java:1)
  at androidx.compose.runtime.snapshots.Snapshot$Companion.observe (Snapshot.java:1)
  at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe (SnapshotStateObserver.java:1)
  at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads (SnapshotStateObserver.java:1)
  at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release (OwnerSnapshotObserver.java:1)
  at androidx.compose.ui.node.OwnerSnapshotObserver.observeMeasureSnapshotReads$ui_release (OwnerSnapshotObserver.java:1)
  at androidx.compose.ui.node.LayoutNodeLayoutDelegate.performMeasure-BRTryo0 (LayoutNodeLayoutDelegate.java:1)
  at androidx.compose.ui.node.LayoutNodeLayoutDelegate.access$performMeasure-BRTryo0 (LayoutNodeLayoutDelegate.java:1)
  at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.remeasure-BRTryo0 (LayoutNodeLayoutDelegate.java:1)
  at androidx.compose.ui.node.LayoutNode.remeasure-_Sx5XlM$ui_release (LayoutNode.java:1)
  at androidx.compose.ui.node.MeasureAndLayoutDelegate.doRemeasure-sdFAvZA (MeasureAndLayoutDelegate.java:1)
  at androidx.compose.ui.node.MeasureAndLayoutDelegate.remeasureOnly (MeasureAndLayoutDelegate.java:1)
  at androidx.compose.ui.node.MeasureAndLayoutDelegate.measureOnly (MeasureAndLayoutDelegate.java:1)
  at androidx.compose.ui.platform.AndroidComposeView.onMeasure (AndroidComposeView.java:1)
  at android.view.View.measure (View.java:28571)
  at androidx.compose.ui.platform.AbstractComposeView.internalOnMeasure$ui_release (AbstractComposeView.java:1)
  at androidx.compose.ui.platform.AbstractComposeView.onMeasure (AbstractComposeView.java:1)
  at android.view.View.measure (View.java:28571)
  at android.view.ViewGroup.measureChildWithMargins (ViewGroup.java:7390)
  at android.widget.FrameLayout.onMeasure (FrameLayout.java:194)
  at android.view.View.measure (View.java:28571)
  at android.view.ViewGroup.measureChildWithMargins (ViewGroup.java:7390)
  at android.widget.LinearLayout.measureChildBeforeLayout (LinearLayout.java:1608)
  at android.widget.LinearLayout.measureVertical (LinearLayout.java:878)
  at android.widget.LinearLayout.onMeasure (LinearLayout.java:721)
  at android.view.View.measure (View.java:28571)
  at android.view.ViewGroup.measureChildWithMargins (ViewGroup.java:7390)
  at android.widget.FrameLayout.onMeasure (FrameLayout.java:194)
  at com.android.internal.policy.DecorView.onMeasure (DecorView.java:1036)
  at android.view.View.measure (View.java:28571)
  at android.view.ViewRootImpl.performMeasure (ViewRootImpl.java:5203)
  at android.view.ViewRootImpl.measureHierarchy (ViewRootImpl.java:3603)
  at android.view.ViewRootImpl.performTraversals (ViewRootImpl.java:3973)
  at android.view.ViewRootImpl.doTraversal (ViewRootImpl.java:3288)
  at android.view.ViewRootImpl$TraversalRunnable.run (ViewRootImpl.java:11344)
  at android.view.Choreographer$CallbackRecord.run (Choreographer.java:1689)
  at android.view.Choreographer$CallbackRecord.run (Choreographer.java:1698)
  at android.view.Choreographer.doCallbacks (Choreographer.java:1153)
  at android.view.Choreographer.doFrame (Choreographer.java:1079)
  at android.view.Choreographer$FrameDisplayEventReceiver.run (Choreographer.java:1646)
  at android.os.Handler.handleCallback (Handler.java:958)
  at android.os.Handler.dispatchMessage (Handler.java:99)
  at android.os.Looper.loopOnce (Looper.java:230)
  at android.os.Looper.loop (Looper.java:319)
  at android.app.ActivityThread.main (ActivityThread.java:8919)
  at java.lang.reflect.Method.invoke
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:578)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1103)

Additional context

I configure RevenueCat in main with this:

  Future<void> configure() async {

      PurchasesConfiguration configuration;

      if (Platform.isAndroid) {
        configuration = PurchasesConfiguration(googleApiKey)..appUserID = null
          ..purchasesAreCompletedBy = const PurchasesAreCompletedByRevenueCat();
      } else if (Platform.isIOS) {
        configuration = PurchasesConfiguration(appleApiKey)..appUserID = null
          ..purchasesAreCompletedBy = const PurchasesAreCompletedByRevenueCat();
      } else {
        throw UnsupportedError('Unsupported platform');
      }

      await Purchases.configure(configuration);

  }

and when I'm showing paywall I use that code:

  Future<bool> presentPaywall(
      BuildContext context, String previousScreen) async {
    await checkPurchaseConfiguration();

    bool purchaseOrRestoreSuccess = false;
    final completer = Completer<bool>();

    CustomerInfo customerInfo = await Purchases.getCustomerInfo();
    if (customerInfo.entitlements.all[entitlementID]?.isActive != true) {
      final remoteConfigHelper = await RemoteConfigHelper.getInstance();
      await remoteConfigHelper.updateConfigIfNeeded();

      if (remoteConfigHelper.showRevenueCatPaywall(previousScreen)) {
        try {
          await context.router.push(
            ParentalGateRoute(
              onVerified: () async {
                final paywallResult = await RevenueCatUI.presentPaywall(
                  displayCloseButton: true,
                );
                FirebaseAnalytics.instance.logEvent(
                  name: 'view_rc_subscription_screen',
                  parameters: {
                    'timestamp': DateTime.now().toIso8601String(),
                    'previous_screen': previousScreen ?? 'none',
                  },
                );
                if (paywallResult == PaywallResult.purchased ||
                    paywallResult == PaywallResult.restored) {
                  appData.updatePremiumStatus(ref, true);
                  FirebaseAnalytics.instance.logEvent(
                    name: 'trial_activated_rc',
                    parameters: {
                      'timestamp': DateTime.now().toIso8601String(),
                      'paywallResult': paywallResult.toString(),
                      'previous_screen': previousScreen ?? 'none',
                    },
                  );
                  purchaseOrRestoreSuccess = true;
                }
                // Mark the completer as complete after handling the paywall result
                completer.complete(purchaseOrRestoreSuccess);
              },
            ),
          );
        } catch (e) {
          // If there's an error (e.g., network issue), log it and fall back to the custom subscription screen
          print("Error while fetching CustomerInfo or RevenueCat paywall : $e");
          FirebaseCrashlytics.instance.recordError(e, null);
          // Fallback: Show your own subscription screen if there's a network error or other issue
          bool? result = await context.router.push<bool>(
            SubscriptionRoute(previousScreen: previousScreen),
          );
          purchaseOrRestoreSuccess = result ?? false;
          completer.complete(purchaseOrRestoreSuccess);
        }
      } else {
        bool? result = await context.router.push<bool>(
          SubscriptionRoute(previousScreen: previousScreen),
        );
        purchaseOrRestoreSuccess = result ?? false;
        completer.complete(purchaseOrRestoreSuccess);
        // }
      }
    } else {
      // Complete the completer if the user already has premium active
      completer.complete(false);
    }

    // Wait for the paywall process to finish before returning
    return completer.future;
  }
}

  Future<void> checkPurchaseConfiguration() async {
    bool isConfigured = await Purchases.isConfigured;
    if (!isConfigured) {
      PurchasesConfiguration configuration;
      if (Platform.isAndroid) {
        configuration = PurchasesConfiguration(googleApiKey);
      } else {
        configuration = PurchasesConfiguration(appleApiKey);
      }
      await Purchases.configure(configuration);
    }
  }
@hirosz hirosz added the bug Something isn't working label Oct 17, 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!

@Jethro87
Copy link

Hi @hirosz, thank you for reporting this and sharing the relevant code. Is your paywall possibly shown in a different snippet than the one you shared? For example, are you calling RevenueCatUI.presentPaywall anywhere else, that possibly isn't doing the isConfigured check?

Also, in your main flow, how quickly is your paywall shown after configuring the RevenueCat SDK? For example, after the configure block, is the paywall immediately shown afterwards? Or is there a delay in attempting to show it?

@hirosz
Copy link
Author

hirosz commented Nov 10, 2024

Hi! I use RevenueCatUI.presentPaywall only in that one place. I use configure in main and show paywall much later, depending on the user need to see first-time instructions.

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

3 participants