v4.3.0
💥 Breaking changes
(android) Google Play in-app messages are shown automatically
The native Android SDK now requests Google Play in-app messages, such as a declined payment notice, every time an Activity starts. To keep the previous behavior, activate with storeMessagesHandling: 'manual' — messages are then shown only when you call showStoreMessages() (see Store messages below).
await adapty.activate('PUBLIC_SDK_KEY', {
storeMessagesHandling: 'manual',
});Deprecated getOnboardingForDefaultAudience no longer accepts loadTimeoutMs
✨ New Features
(iOS) Monthly billing with a 12-month commitment
Subscriptions can now be sold with monthly billing and a 12-month commitment (iOS 26.4+, apps built with Xcode 26.5 or later).
👉 Read how 12-month commitment plans work and how to set one up.
- Products:
product.subscription?.ios?.pricingTermsdescribes the billing plan of a paywall product —billingPlan,billingPrice,billingPeriodand the totalcommitmentInfo.makePurchasepasses the product's billing plan to the App Store, no extra params needed. - Profile: access levels and subscriptions bought on the App Store report
billingPlanandcommitmentInfo— the current billing period, the total number of periods and when the commitment expires.
import { adapty, SubscriptionBillingPlan } from 'react-native-adapty';
const products = await adapty.getPaywallProducts(flow);
for (const product of products) {
const terms = product.subscription?.ios?.pricingTerms;
if (terms?.billingPlan === SubscriptionBillingPlan.Monthly) {
// e.g. "$6.99 / month, $83.88 for 12 months"
console.log(terms.billingPrice.localizedString, terms.commitmentInfo.price.localizedString);
}
}
const profile = await adapty.getProfile();
const commitment = profile.accessLevels?.['premium']?.commitmentInfo;
if (commitment) {
const { billingPeriodNumber, totalBillingPeriods, expiresAt } = commitment;
}Store messages: choose when system messages appear
With storeMessagesHandling: 'manual' the system store messages — price increase consent, billing issue and win-back offer on iOS, Google Play in-app messages such as a declined payment on Android — are held until your app is ready to show them, e.g. after onboarding or a checkout flow.
👉 Read more in Control store messages.
import { adapty } from 'react-native-adapty';
await adapty.activate('PUBLIC_SDK_KEY', {
storeMessagesHandling: 'manual',
});
// later, when the app is ready
const types = await adapty.getPendingStoreMessageTypes();
if (types === null) {
// Android: Google Play can't report pending messages, let it decide
await adapty.showStoreMessages();
} else if (types.includes('billing_issue')) {
// iOS: explain the billing issue in your own UI first, then show the system message
await adapty.showStoreMessages({ ios: { filter: ['billing_issue'] } });
}getPendingStoreMessageTypes()resolves[]when there is nothing to show andnullon Android, where the store can't tell.showStoreMessages()shows every pending message;ios.filterlimits the types on iOS and is ignored on Android.- On iOS store messages require iOS 16+;
storeMessagesHandlinghas no effect below it.
Preload flows
preloadFlows warms the cache for several placements at once, fetching them in parallel. Useful for cross-placement A/B tests.
👉 Read more in Preload placements.
import { adapty, FetchPolicy } from 'react-native-adapty';
await adapty.preloadFlows(['onboarding', 'settings_paywall']);
// later
const flow = await adapty.getFlow('settings_paywall', {
fetchPolicy: FetchPolicy.ReturnCacheDataElseLoad,
});getFlowreads the warmed cache only withReturnCacheDataElseLoadorReturnCacheDataIfNotExpiredElseLoad. With the defaultReloadRevalidatingCacheDatait still goes to the network, and the preloaded copy serves as an offline fallback.- Only the placement data is cached — the flow UI and images are still loaded when the view is created.
Keep a modal flow view alive between presentations
For a flow presented modally with createFlowView, dismiss({ destroy: false }) hides the view but keeps it, together with its event handlers. Presenting it again resumes on the screen the user left, with the state the flow had built up. Release it with the new destroy() when you no longer need it.
👉 Read more in Keep a closed flow for later.
import { createFlowView } from 'react-native-adapty';
const view = await createFlowView(flow);
await view.present();
await view.dismiss({ destroy: false }); // hide, keep the view
await view.present(); // resumes where the user left off
await view.destroy(); // release it for gooddismiss() without options destroys the view, as before.
(iOS) Report a transaction with its signed StoreKit 2 representation
reportTransaction accepts the StoreKit 2 JWS representation of the transaction, so the signed transaction is sent to Adapty.
👉 Read more in Report transactions in Observer Mode.
Custom log sinks
setLogLevel takes an optional logger config to forward the SDK's JS logs to your own sinks: setLogLevel('verbose', { logger: { sinks: [consoleLogSink, mySink] } }).
🛠 Improvements
- (iOS) Flows open faster: products now load while the flow is being prepared.
- (android) Flows. The hero cover moves with a parallax effect when the screen scrolls.
- (android) Flows. The footer background of hero and flat screens is shown only while scrollable content is beneath it.
🐛 Fixes
- A log level set with
setLogLevelbeforeactivatewas reset whenactivategot nologLevel. - (android) A paywall or onboarding presented with
present()disappeared when the app was reopened from the launcher icon. Flows and onboardings are now presented inside the host activity. - (iOS) A crash while choosing a placement variation (
load from misaligned raw pointer). - (iOS) Flows. A flow could crash or lose a product when the same product appeared on more than one screen.
- (iOS) Flows. Products did not load again after a failed attempt.
- (iOS) Flows. On iOS 16 and earlier a flow could show a black screen instead of the next screen.
- (iOS) Flows. On iOS 17 heavy screens could freeze the app.
- (iOS) Flows. During a transition to a heavy screen the outgoing screen vanished mid-animation, showing a black background.
- (iOS) Flows. Screens with many conditional sections were slow to appear.
- (iOS) Flows. Screen transitions and bottom sheets could drop to 60 fps on ProMotion displays and cut the end of their animation short.
- (iOS) Flows. A timer action fired again after navigating between screens.
- (iOS) Flows. A date and time picker crashed the app when its value or bounds were out of range.
- (iOS) Flows. A video flickered or jumped when its preview image gave way to playback.
- (iOS) Flows. A line height below the font's natural line height had no effect.
- (iOS) Flows. On iOS 26+ text fields ignored right-to-left alignment.
- (iOS) Flows. Gradient borders ignored the configured thickness, and conic gradients started at the wrong angle.
- (android) Flows. Various rendering fixes and stability improvements.