There is no singleton instance
- [X] I have updated Purchases SDK to the latest version
- [X] I have read the Contribution Guidelines
- [X] I have searched the Community
- [X] I have read docs.revenuecat.com
- [X] I have searched for existing Github issues
‼️ 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
- [X] Output of
flutter doctorDoctor 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!
- [X] Version of
purchases-flutterpurchases_flutter: ^6.29.0 - [X] Testing device version e.g.: iOS 15.5, Android API 30, etc. Android 12-14. Might also affect older Android versions
- [X] How often the issue occurs- every one of your customers is impacted? Only in dev? Affecting 3% of users in production according to Crashlytics
- [X] 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 - [X] 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.
👀 We've just linked this issue to our internal tracker and notified the team. Thank you for reporting, we're checking this out!
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
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)
Thank you! Let me ask a mobile engineer for input on this.
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?
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.
This issue still persists, We had checked the isConfigured status before executing any rc methods. Please fix !!
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
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 am experiencing the same. I call
await Purchases.configurein 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.
I can confirm that I'm also experiencing the same issue.
+1
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
I am experiencing the same. I call
await Purchases.configurein 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
I am experiencing the same. I call
await Purchases.configurein 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.
There is still a problem. Please open it
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.
Hello everyone, just in case anyone looking for a solution for the above issue, I faced the same issue and I fixed it by:
- upgrade purchases_flutter to 8.2.2 and purchases_ui_flutter: 8.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
List<String> 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, );
I am using purchases_flutter: 8.4.0 and this issue still exists on Android
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.
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?
@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!
@diegofrata yes that would've been annoying! Let's hope you're right
Looks like it's been fixed with the latest release 🎉 Thanks @tonidero 🙏