Migrate Adapty Unity SDK to v4.1
Adapty Unity SDK 4.1 is the first stable release of the 4.x line — 4.0 was released only as a beta, so if you’re on 3.x, migrate straight to 4.1. This guide covers the whole migration: the flows introduced in 4.0 and the 4.1 changes on top of them.
The 4.x line introduces flows and renames the paywall APIs accordingly. The new APIs work with flows, and they still work with paywalls from the old builder — no setup changes are required on the Adapty Dashboard side. On top of that, 4.1 renames the external attribution APIs, makes Adapty Attribution opt-in, adds a required listener method, and changes the fallback file format.
Coming from the 4.0 beta? Replace the pinned beta tag with the 4.1.0 install, then only four sections apply: the new listener method, renamed external attribution APIs, Adapty Attribution is disabled by default, and fallback files.
Quick reference
| v3 | v4.1 |
|---|---|
Adapty.GetPaywall(placementId, locale, ...) | Adapty.GetFlow(placementId, ...) |
Adapty.GetPaywallForDefaultAudience(placementId, locale, ...) | Adapty.GetFlowForDefaultAudience(placementId, ...) |
Adapty.GetPaywallProducts(paywall, ...) | Adapty.GetPaywallProducts(flow, ...) |
Adapty.LogShowPaywall(paywall, ...) | Adapty.LogShowFlow(flow, ...) |
AdaptyPaywall | AdaptyFlow |
AdaptyUI.CreatePaywallView(paywall, ...) | AdaptyUI.CreateFlowView(flow, ...) |
AdaptyUICreatePaywallViewParameters | AdaptyUICreateFlowViewParameters |
AdaptyUIPaywallView | AdaptyUIFlowView |
AdaptyUI.PresentPaywallView(view, ...) / DismissPaywallView(view, ...) | AdaptyUI.PresentFlowView(view, ...) / DismissFlowView(view, ...) |
Adapty.SetPaywallsEventsListener(listener) | Adapty.SetFlowsEventsListener(listener) |
AdaptyPaywallsEventsListener | IAdaptyFlowsEventsListener |
AdaptyEventListener | IAdaptyEventListener, with a new required OnReceivePromotedPurchase method |
AdaptyOnboardingsEventsListener | IAdaptyOnboardingsEventsListener |
PaywallViewDidPerformAction, PaywallViewDidAppear, and other PaywallView... callbacks | FlowViewDidPerformAction, FlowViewDidAppear, and other FlowView... callbacks |
PaywallViewDidFailRendering | FlowViewDidReceiveError |
Adapty.UpdateAttribution(data, source, ...) with a string source | Adapty.UpdateExternalAttribution(jsonString, provider, ...) with an AdaptyExternalAttributionProvider |
AdaptyProfile.AppliedAttributionSources as IReadOnlyList<string> | AdaptyProfile.AppliedExternalAttributionProviders as IReadOnlyList<AdaptyExternalAttributionProvider> |
| Adapty Attribution enabled automatically | disabled by default — opt in with Builder.SetAdaptyAttributionEnabled(true) |
| Fallback file downloaded for 3.x | new fallback file format — download the file again |
Adapty.SetFallbackPaywalls(...) (deprecated in v3) | removed — use Adapty.SetFallback(fileName, ...) |
Builder.SetIDFACollectionDisabled(...) (deprecated in v3) | removed — use Builder.SetAppleIDFACollectionDisabled(...) |
paywall.Products (a list of AdaptyProductReference) | removed — use ProductIdentifiers or VendorProductIds, or call GetPaywallProducts(flow) for full products |
AdaptyProductReference | removed as a public type — see Data model |
paywall.RemoteConfigString | removed — use flow.RemoteConfig?.Data |
AdaptyPaywallProduct keeps its name — products still belong to a flow, and GetPaywallProducts keeps its name too, now taking an AdaptyFlow. The GetFlow and GetFlowForDefaultAudience methods no longer take a locale parameter. The purchase and profile APIs (MakePurchase, RestorePurchases, GetProfile, Identify, UpdateProfile) are unchanged. SetFallback keeps its signature, but the file it reads must be downloaded again — see Fallback files. The onboarding methods still work but are deprecated — see Onboarding API deprecation. Some default behaviors changed — see Default behavior changes.
Installation
To install SDK 4.1 via the Unity Package Manager, append the version tag to the Git URL:
https://github.com/adaptyteam/AdaptySDK-Unity.git?path=/Packages/com.adapty.unity-sdk#4.1.0
If you install via the Unity package, download adapty-unity-plugin-4.1.0.unitypackage from the 4.1.0 release. See Install Adapty SDK for the full setup.
Two build-setup changes come with 4.x:
- iOS dependencies switch to Swift Package Manager. The native Adapty iOS SDK is declared as a remote Swift package instead of a CocoaPods pod. Update the External Dependency Manager to 1.2.188 or later — earlier versions don’t support Swift Package Manager dependencies. The CocoaPods steps (
iOS Resolver -> Install Cocoapods, openingUnity-iPhone.xcworkspace) no longer apply. Building for iOS now requires Xcode 26 or later, since the Swift package is built with Swift tools 6.2. - The iOS deployment target must be 15.0 or later. A new build validator in the Unity Editor stops the iOS build if the target is lower.
The underlying native Adapty SDKs are bumped to 4.x on both platforms and are resolved automatically — no other build changes are needed.
Fetching flows
GetPaywall → GetFlow
The returned type changes from AdaptyPaywall to AdaptyFlow, and the locale parameter is removed — when you render a flow, the locale is resolved automatically; for custom paywalls, all locales are returned in flow.RemoteConfigs:
- Adapty.GetPaywall("YOUR_PLACEMENT_ID", "en", (paywall, error) => {
+ Adapty.GetFlow("YOUR_PLACEMENT_ID", (flow, error) => {
if (error != null) {
// handle the error
return;
}
- // use the paywall
+ // use the flow
});
GetPaywallForDefaultAudience is renamed the same way:
- Adapty.GetPaywallForDefaultAudience("YOUR_PLACEMENT_ID", "en", (paywall, error) => { /* ... */ });
+ Adapty.GetFlowForDefaultAudience("YOUR_PLACEMENT_ID", (flow, error) => { /* ... */ });
GetPaywallProducts(paywall) → GetPaywallProducts(flow)
GetPaywallProducts keeps its name but now takes an AdaptyFlow:
- Adapty.GetPaywallProducts(paywall, (products, error) => {
+ Adapty.GetPaywallProducts(flow, (products, error) => {
if (error != null) {
// handle the error
return;
}
// use the products
});
Data model
GetFlow returns an AdaptyFlow instead of an AdaptyPaywall, and the object shape changed:
v3 AdaptyPaywall property | v4 AdaptyFlow property | Action |
|---|---|---|
RemoteConfig (single, nullable) | RemoteConfigs (list) | A flow carries one remote config per configured language. Read the one that matches the user from flow.RemoteConfigs. The flow.RemoteConfig shortcut returns the first entry. |
| (new) | Paywalls (list of AdaptyFlowPaywall) | Each entry is one paywall variation in the flow, with its own Name, VariationId, and ProductIdentifiers. The web paywall methods take an AdaptyFlowPaywall — see Web paywall methods. |
ProductIdentifiers, VendorProductIds | kept | On AdaptyFlow, these aggregate the products across all paywall variations. Each variation also exposes its own ProductIdentifiers and VendorProductIds. To fetch products, keep calling GetPaywallProducts(flow). |
HasViewConfiguration | removed | Remove any HasViewConfiguration check from your code — CreateFlowView returns an error instead (see Displaying flows). |
Products (list of AdaptyProductReference) | removed | AdaptyProductReference is no longer public, and with it the PromotionalOfferId, WinBackOfferId, and AndroidOfferId values it carried. Use ProductIdentifiers — a list of AdaptyProductIdentifier with VendorProductId and the Android-only BasePlanId (v3’s AndroidBasePlanId) — or call GetPaywallProducts(flow) when you need full AdaptyPaywallProduct objects with prices and offers. |
RemoteConfigString | removed | Read the string from the remote config itself: flow.RemoteConfig?.Data, or the matching entry in flow.RemoteConfigs. |
| (new) | FlowVersionId (nullable) | The version identifier of the flow, or null when it isn’t available. |
AdaptyPaywallProduct gains one field: FlowProductId, the product’s identifier within the flow, which is null for products that don’t belong to a flow.
Web paywall methods
OpenWebPaywall and CreateWebPaywallUrl keep their names, but the paywall argument now takes an AdaptyFlowPaywall — one of the variations in flow.Paywalls. You can still pass an AdaptyPaywallProduct instead:
- Adapty.OpenWebPaywall(paywall, AdaptyWebPresentation.ExternalBrowser, (error) => { /* ... */ });
+ var flowPaywall = flow.Paywalls.FirstOrDefault();
+ if (flowPaywall != null) {
+ Adapty.OpenWebPaywall(flowPaywall, AdaptyWebPresentation.ExternalBrowser, (error) => { /* ... */ });
+ }
Tracking flow views
LogShowPaywall → LogShowFlow
LogShowPaywall is renamed to LogShowFlow and now takes an AdaptyFlow. The event is still logged against the same variation, so existing funnel and A/B test metrics continue to work without dashboard changes.
- Adapty.LogShowPaywall(paywall, (error) => { /* ... */ });
+ Adapty.LogShowFlow(flow, (error) => { /* ... */ });
As in v3, you do not need to call this method when displaying flows or paywalls rendered by Adapty — Adapty tracks those views automatically.
Displaying flows
CreatePaywallView → CreateFlowView
Rename the factory method and pass the AdaptyFlow. The returned view type is renamed from AdaptyUIPaywallView to AdaptyUIFlowView, but its methods (Present, Dismiss) are unchanged, and the optional parameters object keeps the same fields (LoadTimeout, PreloadProducts, CustomTags, CustomTimers, CustomAssets, ProductPurchaseParameters) under the new AdaptyUICreateFlowViewParameters name, plus two new ones — Locale and EnableSafeAreaPaddings:
CustomTimers still exists, but it only affects legacy Paywall Builder paywalls. A flow’s countdown timer runs on the behavior set in the Flow & Paywall Builder, so a flow ignores whatever you pass here.
- AdaptyUI.CreatePaywallView(paywall, parameters, (view, error) => {
+ AdaptyUI.CreateFlowView(flow, parameters, (view, error) => {
if (error != null) {
// handle the error
return;
}
view.Present((error) => { /* handle the error */ });
});
CreateFlowView returns an error if the flow has no view configured — this replaces the v3 HasViewConfiguration check:
- if (paywall.HasViewConfiguration) {
- AdaptyUI.CreatePaywallView(paywall, null, (view, error) => { /* ... */ });
- }
+ AdaptyUI.CreateFlowView(flow, (view, error) => {
+ if (error != null) {
+ // the flow has no view configured, or view creation failed
+ return;
+ }
+ view.Present((error) => { /* handle the error */ });
+ });
A flow view is single-use: after you call Dismiss, the view is destroyed, so call CreateFlowView again to present the flow once more.
Android safe-area paddings
AdaptyUICreateFlowViewParameters adds EnableSafeAreaPaddings, which controls Android safe-area paddings at runtime. It is ignored on iOS and defaults to true:
var parameters = new AdaptyUICreateFlowViewParameters()
.SetEnableSafeAreaPaddings(false);
Handling events
Listener interfaces now follow the C# I-prefix convention, and no legacy aliases are kept — rename AdaptyEventListener to IAdaptyEventListener and AdaptyOnboardingsEventsListener to IAdaptyOnboardingsEventsListener wherever you implement them.
The flow events listener is renamed from AdaptyPaywallsEventsListener to IAdaptyFlowsEventsListener, its registration method from SetPaywallsEventsListener to SetFlowsEventsListener, and its callbacks change the PaywallView prefix to FlowView. Existing handler bodies don’t need code changes — just rename the interface and the methods:
- public class MyListener : MonoBehaviour, AdaptyPaywallsEventsListener {
- public void PaywallViewDidFinishPurchase(
- AdaptyUIPaywallView view,
+ public class MyListener : MonoBehaviour, IAdaptyFlowsEventsListener {
+ public void FlowViewDidFinishPurchase(
+ AdaptyUIFlowView view,
AdaptyPaywallProduct product,
AdaptyPurchaseResult purchasedResult
) {
// custom logic after purchase
}
// ...
}
- Adapty.SetPaywallsEventsListener(myListener);
+ Adapty.SetFlowsEventsListener(myListener);
One callback is renamed: PaywallViewDidFailRendering becomes FlowViewDidReceiveError. It fires for the same rendering errors as before, plus other non-purchase runtime errors:
- public void PaywallViewDidFailRendering(AdaptyUIPaywallView view, AdaptyError error) { }
+ public void FlowViewDidReceiveError(AdaptyUIFlowView view, AdaptyError error) { }
See Handle flow & paywall events for the full list of callbacks.
New required method: OnReceivePromotedPurchase
Starting from SDK version 4.1, IAdaptyEventListener has one more method, so every class implementing it stops compiling until you add:
public void OnReceivePromotedPurchase(AdaptyPromotedProduct product) {
// The user tapped one of your in-app purchases on your App Store product page.
// Complete the purchase through Adapty:
Adapty.MakePromotedPurchase(product, (result, error) => { /* ... */ });
}
The method is for App Store promoted in-app purchases and is never called on Android. Don’t leave the body empty: on iOS 16.4 and later the SDK delivers the purchase here and waits for you to complete it, so an empty body drops a purchase the user already started. Earlier versions completed such purchases automatically. See Promoted in-app purchases from the App Store.
New APIs
Adapty.SetObserverModeResolver(...)with anIAdaptyUIObserverModeResolver— drive purchases and restores initiated from flows while the SDK runs in Observer mode. Previously this was available only in the native iOS and Android SDKs. See Present flows in Observer mode.Adapty.SetSystemRequestsHandler(...)with anIAdaptyUISystemRequestsHandler— reserved for system requests from a flow: OS permission prompts (FlowViewDidAskPermission) and app review requests (FlowViewDidRequestAppReview). Flows don’t trigger these requests yet, so you don’t need to register a handler.AdaptyUICreateFlowViewParameters.Locale(set it withSetLocale) — render a flow or paywall with a specific Builder localization instead of the flow’s default one. A flow is localized when its view is created, so this is the only place to choose its localization, and the created view reports the localization it was built with inview.Locale. See Use localizations and locale codes.- The new
FlowViewDidReceiveAnalyticEventcallback onIAdaptyFlowsEventsListenerreports analytics events from a flow, starting with a screen view for every screen a user opens. See Track flow screen views. AdaptyUI.OpenUrl(url, openIn, ...)andAdaptyUI.RequestAppReview(...)— the native handling behindopen_urlactions and app review requests. CallOpenUrlfromFlowViewDidPerformActionto keep the default URL behavior;RequestAppReviewbacks the default app-review prompt, which flows don’t trigger yet.
Renamed external attribution APIs
Starting from SDK version 4.1, the APIs for passing attribution data from an external provider (Adjust, AppsFlyer, Branch, Tenjin, or a custom one) are renamed to match the native SDKs, and the provider changes from a string to a type. There are no deprecated aliases, so existing call sites stop compiling until you update them:
| Before 4.1 | 4.1 |
|---|---|
Adapty.UpdateAttribution(data, source, ...) | Adapty.UpdateExternalAttribution(jsonString, provider, ...) |
source as a string | provider as an AdaptyExternalAttributionProvider |
AdaptyProfile.AppliedAttributionSources as IReadOnlyList<string> | AdaptyProfile.AppliedExternalAttributionProviders as IReadOnlyList<AdaptyExternalAttributionProvider> |
Renaming the method alone isn’t enough — swap the provider argument in the same edit:
- Adapty.UpdateAttribution(attributionJsonString, "adjust", (error) => { /* ... */ });
+ Adapty.UpdateExternalAttribution(attributionJsonString, AdaptyExternalAttributionProvider.Adjust, (error) => { /* ... */ });
AdaptyExternalAttributionProvider carries the identifier the backend knows the provider by, with six shared instances: AppleAds (apple_search_ads), Adjust, Appsflyer, Branch, Tenjin, and Custom. For a provider Adapty adds after this SDK release, construct one from its identifier — new AdaptyExternalAttributionProvider("your_provider") — and it reaches the backend unchanged. Surrounding whitespace is trimmed.
Attribution data goes in as a serialized JSON string. If you hold it as a dictionary, serialize it first:
var attributionJsonString = Newtonsoft.Json.JsonConvert.SerializeObject(attribution);
On the profile side, read the applied providers through the new type:
- if (profile.AppliedAttributionSources.Contains("apple_search_ads")) {
+ if (profile.AppliedExternalAttributionProviders.Contains(AdaptyExternalAttributionProvider.AppleAds)) {
// Apple Ads attribution has been applied
}
Adapty Attribution is disabled by default
If you use Adapty Attribution and update to SDK 4.1 without opting in, it breaks silently — installs stop registering, and nothing warns you.
In earlier versions, the SDK registered installs for Adapty Attribution automatically. Starting from SDK version 4.1, this is off by default: the SDK doesn’t register installs, the OnInstallationDetailsSuccess and OnInstallationDetailsFail listener callbacks never fire, and GetCurrentInstallationStatus returns the NotAvailable status.
If you use Adapty Attribution, enable it when activating the SDK:
var builder = new AdaptyConfiguration.Builder("YOUR_PUBLIC_SDK_KEY")
.SetAdaptyAttributionEnabled(true);
If you don’t use Adapty Attribution, no changes are needed.
Fallback files
The fallback file format changed in SDK 4.1. Download the iOS and Android fallback files again from Placements > Fallbacks and replace the ones in Assets/StreamingAssets, even if you already downloaded them for an earlier version.
This step produces no compile error. If you skip it, SetFallback reports DecodingFailed (adapty_code: 2006), and every placement loses its fallback.
Default behavior changes
These changes do not cause compile errors, so test them at runtime:
- Purchase completion: In v3, the view was dismissed automatically after a successful purchase. In v4, a flow stays open after a purchase or an error until you dismiss it — the SDK applies no default behavior. Call
view.Dismiss(...)yourself inFlowViewDidFinishPurchaseonce the user gets access. - Android system back: The system back button (or back gesture) is delivered to
FlowViewDidPerformActionas aSystemBackaction and no longer closes a flow on its own — matching iOS, where a flow can’t be dismissed by a system gesture. Give users an explicit way out (a Close button or anon_device_backaction), or dismiss the view yourself when handling the action. - Views are single-use: After
Dismiss, the view is destroyed. CallCreateFlowViewagain to present the flow once more. - Observer mode transactions:
ReportTransactionno longer surfaces a decoding error on success — in v3 the success response was incorrectly parsed, so a successful report always completed with an error.
Onboarding API deprecation
The legacy onboarding API is deprecated in v4 in favor of the Flow & Paywall Builder. It still works, but will be removed in a future release, so plan migration of your onboardings to the Flow & Paywall Builder.
Deprecated symbols: GetOnboarding, GetOnboardingForDefaultAudience, AdaptyUI.CreateOnboardingView, AdaptyUI.PresentOnboardingView, AdaptyUI.DismissOnboardingView, and Adapty.SetOnboardingsEventsListener.