Hey @MichaelP,
This usually relates to how the RevenueCat SDK caches subscription information (CustomerInfo). Below is a breakdown of how caching works, and the best practices for keeping it up to date reliably across app sessions.
CustomerInfo Caching
The SDK caches CustomerInfo locally on device to reduce network overhead and improve performance. This cache is automatically refreshed when:
- the app moves to the foregroung
- you explicitly call getCustomerInfo()
- a purchase or restore is completed
However, if a user’s subscription status changes while the app is completely closed (e.g., a trial converts or a renewal occurs), the cached data will not reflect that change until one of the above triggers occurs. To this, some suggested best practises into keeping it accurate/up to date are:
1. Use a CustomerInfo Listener
The most reliable way to stay in sync during an app session is to listen for updates using addCustomerInfoUpdateListener (docs here). This ensures your app state reacts to any updates without manual polling or re-fetching. An example in code could be:
// React Native example
useEffect(() => {
const listener = Purchases.addCustomerInfoUpdateListener((customerInfo) => {
const hasAccess = customerInfo.entitlements.active."premium_access"] != null;
setHasAccess(hasAccess);
});
return () => listener.remove();
}, )]);
2. Fetch CustomerInfo at Key Times
In addition to the listener, you should call getCustomerInfo() manually:
- on app launch or after login - which seems to be your case
- when navigating to a paywalled feature
- after any account change (login/logout)
This ensures your access logic is based on the most recent available data, especially after long periods in the background. Like so:
const customerInfo = await Purchases.getCustomerInfo();
const hasAccess = customerInfo.entitlements.activem"premium_access"] != null;
3. Enable Server-to-Server Notifications
If you're concerned about subscription changes happening outside of the app (e.g., upgrades, renewals, cancellations), consider setting up Server-to-Server (S2S) notifications. These ensure RevenueCat’s backend stays up-to-date with Apple and Google’s stores, even when the app isn’t running.
Once the backend is in sync, any subsequent call to getCustomerInfo() will reflect the correct state.
Docs: Platform Server Notifications
Optional: Forcing a Cache Refresh
If you need to guarantee a fresh subscription state due to a critical flow or after a state mismatch, you can manually invalidate the SDK’s cache and fetch updated data:
await Purchases.invalidateCustomerInfoCache();
const customerInfo = await Purchases.getCustomerInfo();
This method works, but should be used sparingly to avoid unnecessary network requests. For example, only after an external event or when debugging desync issues.
Some extra context around caching, which might be good to be aware/consider:
- the cache persists across app launches
- foregrounded apps auto-refresh every 5 minutes
- backgrounded apps refresh after ~25 hours
- any purchase or restore triggers an immediate update
Docs: Caching CustomerInfo
Let me know how these changes help you and feel free to reach out if you have any more questions!
Best,
Gui
Thank you! Great information.