github adaptyteam/AdaptySDK-React-Native v4.3.0

4 hours ago

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?.pricingTerms describes the billing plan of a paywall product — billingPlan, billingPrice, billingPeriod and the total commitmentInfo. makePurchase passes the product's billing plan to the App Store, no extra params needed.
  • Profile: access levels and subscriptions bought on the App Store report billingPlan and commitmentInfo — 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 and null on Android, where the store can't tell.
  • showStoreMessages() shows every pending message; ios.filter limits the types on iOS and is ignored on Android.
  • On iOS store messages require iOS 16+; storeMessagesHandling has 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,
});
  • getFlow reads the warmed cache only with ReturnCacheDataElseLoad or ReturnCacheDataIfNotExpiredElseLoad. With the default ReloadRevalidatingCacheData it 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 good

dismiss() 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 setLogLevel before activate was reset when activate got no logLevel.
  • (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.

❗️ Don't forget to update your local fallback file

Don't miss a new AdaptySDK-React-Native release

NewReleases is sending notifications on new releases.