react-native-purchases
react-native-purchases copied to clipboard
Purchases.syncPurchases error: "There is no singleton instance. Make sure you configure Purchases before trying to get the default instance."
- [✅ ] I have updated Purchases SDK to the latest version
- [✅] I have read the Contribution Guidelines
- [✅] I have searched the Community
- [✅] I have read docs.revenuecat.com
- [✅] I have searched for existing Github issues
Describe the bug
Purchases.syncPurchases() Error:
WARN Possible Unhandled Promise Rejection (id: 0):
Error: 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
- Environment
- Platform: iOS and Android
- SDK version: 6.6.5
- OS version:
- Xcode/Android Studio version:
- React Native version: 0.72.4
- SDK installation (CocoaPods + version or manual):
- How widespread is the issue. Percentage of devices affected. 100%
- Debug logs that reproduce the issue
- Steps to reproduce, with a description of expected vs. actual behavior
index.js:
import Purchases from 'react-native-purchases';
Purchases.setLogLevel(Purchases.LOG_LEVEL.DEBUG);
Purchases.configure({apiKey: xxxxxxxxxxxxxxxx});
App.tsx
const storage = new MMKV();
const App = () => {
useEffect(() => {
const init = async () => {
try {
if (!storage.getBoolean(MMKVKeys.HAS_SYNCED_PURCHASES)) {
const configured = await Purchases.isConfigured();
console.log('revenue cat is configured:', configured); // this console.log shows "revenue cat is configured: true"
if (configured) {
await Purchases.syncPurchases();
storage.set(MMKVKeys.HAS_SYNCED_PURCHASES, true);
console.log('mmkv stored:', storage.getBoolean(MMKVKeys.HAS_SYNCED_PURCHASES)); // this console.log shows "mmkv stored: true"
console.log('revenue cat purchases synced') // this console.log is printed successfully;
};
};
} catch (error) {
console.log('caught error', error)
}
};
init();
},[])
};
export default App;
interestingly syncPurchases resolves the promise successfully so the error is not caught by the try/catch block. an unhandled promise warning is thrown AFTER syncPurchases() has supposedly already completed.
- Other information (e.g. stacktraces, related issues, suggestions how to fix, links for us to have context, eg. stackoverflow, etc.)
LOG Platform: ios 16.6.1 Mode: debug
LOG Running "SAMPLE" with {"rootTag":1,"initialProps":{}}
LOG revenue cat is configured: true
LOG mmkv stored: true
LOG revenue cat purchases synced
WARN Possible Unhandled Promise Rejection (id: 0):
Error: 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
Error: 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 call (native)
at UninitializedPurchasesError (http://192.168.0.100:8081/index.bundle//&platform=ios&dev=true&minify=false&modulesOnly=false&runModule=true&app=com.sample:137882:30)
at anonymous (http://192.168.0.100:8081/index.bundle//&platform=ios&dev=true&minify=false&modulesOnly=false&runModule=true&app=com.sample:137668:132)
at call (native)
at step (http://192.168.0.100:8081/index.bundle//&platform=ios&dev=true&minify=false&modulesOnly=false&runModule=true&app=com.sample:136259:23)
at anonymous (http://192.168.0.100:8081/index.bundle//&platform=ios&dev=true&minify=false&modulesOnly=false&runModule=true&app=com.sample:136208:20)
at fulfilled (http://192.168.0.100:8081/index.bundle//&platform=ios&dev=true&minify=false&modulesOnly=false&runModule=true&app=com.sample:136167:30)
at tryCallOne (/Users/distiller/react-native/packages/react-native/sdks/hermes/build_iphoneos/lib/InternalBytecode/InternalBytecode.js:53:16)
at anonymous (/Users/distiller/react-native/packages/react-native/sdks/hermes/build_iphoneos/lib/InternalBytecode/InternalBytecode.js:139:27)
at apply (native)
at anonymous (http://192.168.0.100:8081/index.bundle//&platform=ios&dev=true&minify=false&modulesOnly=false&runModule=true&app=com.sample:34672:26)
at _callTimer (http://192.168.0.100:8081/index.bundle//&platform=ios&dev=true&minify=false&modulesOnly=false&runModule=true&app=com.sample:34551:17)
at _callReactNativeMicrotasksPass (http://192.168.0.100:8081/index.bundle//&platform=ios&dev=true&minify=false&modulesOnly=false&runModule=true&app=com.sample:34596:17)
at callReactNativeMicrotasks (http://192.168.0.100:8081/index.bundle//&platform=ios&dev=true&minify=false&modulesOnly=false&runModule=true&app=com.sample:34802:44)
at __callReactNativeMicrotasks (http://192.168.0.100:8081/index.bundle//&platform=ios&dev=true&minify=false&modulesOnly=false&runModule=true&app=com.sample:3505:46)
at anonymous (http://192.168.0.100:8081/index.bundle//&platform=ios&dev=true&minify=false&modulesOnly=false&runModule=true&app=com.sample:3279:45)
at __guard (http://192.168.0.100:8081/index.bundle//&platform=ios&dev=true&minify=false&modulesOnly=false&runModule=true&app=com.sample:3478:15)
at flushedQueue (http://192.168.0.100:8081/index.bundle//&platform=ios&dev=true&minify=false&modulesOnly=false&runModule=true&app=com.sample:3278:21)
at invokeCallbackAndReturnFlushedQueue (http://192.168.0.100:8081/index.bundle//&platform=ios&dev=true&minify=false&modulesOnly=false&runModule=true&app=com.sample:3272:33)
👀 We've just linked this issue to our internal tracker and notified the team. Thank you for reporting, we're checking this out!
Hello, I'll get some input from our engineers here and get back to you soon.
Hi @skam22, I've been looking into this and haven't been able to find what could possibly be wrong here. Something did catch my attention though:
interestingly syncPurchases resolves the promise successfully so the error is not caught by the try/catch block. an unhandled promise warning is thrown AFTER syncPurchases() has supposedly already completed.
Is it possible that the error is being thrown from a different call to syncPurchases
?
Hi, Could this be due to React rendering components twice in Development mode?
I believe i noticed that Purchases.Configure()
actually triggers a promise rejection if called twice. Even if it says its not returning any promises. So i put Purchases.isConfigured() as a guard around the Configure
- which got rid of the Promise rejection.
Looking at the code, it seems that Purchases.Configure()
is a more or less direct method against the RCT_EXPORT_METHOD - Which is an async operation that always returns void
.
But the async operation probably gets rejected because of an internal error in the iOS code.
@Cnordbo that's definitely interesting. I think this could happen if the components are being rendered twice. I don't see code in the Android SDK that will trigger an exception if configure
is called twice. But in iOS I see that it will throw an exception when in DEBUG
@skam22 are you setting strict mode like indicated in React Docs - Keeping Components Pure? Do you think this could be causing the issues you're seeing?
@vegaro - I am not sure about this, but let me just assume something as well here.
You dont really need to run in strict mode to trigger this, i think, as i am not running StrictMode myself and got this. I would assume (but could be wrong here) that the instance generated in iOS (and probably Android as well) is the same during re-renders.
So while your developing, you would most-likely cause a re-render each time of the affected components. Meaning that if you change something that, depending on your implementation / .Configure point, requires a re-render of your .Configure / Complete re-render, meaning this would get called twice anyway, while still keeping the runtime-instance of Purchases alive.
As a change, would it be possible to allow the .Configure method be called multiple times without throwing an exception, if it really doesnt matter? I do get why it could be a good idea to give some sort of feedback that its beeing called twice, when its only ment to be called once. But does it really affect anything if called twice?