Skip to main content

Migration guide to Adapty iOS SDK 3.2.x

Adapty iOS SDK 3.2.0 is a major release that brought some improvements which however may require some migration steps from you.

  1. Rename Adapty.Configuration to AdaptyConfiguration.
  2. Rename the getViewConfiguration method to getPaywallConfiguration.
  3. Remove the didCancelPurchase and paywall parameters from SwiftUI, and rename the viewConfiguration parameter to paywallConfiguration.
  4. Update how you process promotional in-app purchases from the App Store by removing the defermentCompletion parameter from the AdaptyDelegate method.
  5. Remove the getProductsIntroductoryOfferEligibility method.
  6. Update integration configurations for:

Rename Adapty.Configuration to AdaptyConfiguration

Update the code of Adapty iOS SDK activation in the following way:

// In your AppDelegate class:
import Adapty

let configurationBuilder =
- Adapty.Configuration
+ AdaptyConfiguration
.builder(withAPIKey: "PUBLIC_SDK_KEY")
.with(observerMode: false)
.with(customerUserId: "YOUR_USER_ID")
.with(idfaCollectionDisabled: false)
.with(ipAddressCollectionDisabled: false)

Adapty.activate(with: configurationBuilder) { error in
// handle the error
}

Rename getViewConfiguration method to getPaywallConfiguration

Update the method name to fetch the paywall's viewConfiguration:

import Adapty
import AdaptyUI

guard paywall.hasViewConfiguration else {
// use your custom logic
return
}

do {
- let paywallConfiguration = try await AdaptyUI.getViewConfiguration(
+ let paywallConfiguration = try await AdaptyUI.getPaywallConfiguration(
forPaywall: paywall
)
// use loaded configuration
} catch {
// handle the error
}

For more details about the method, check out Fetch the view configuration of paywall designed using Paywall Builder..

Change parameters in SwiftUI

The didCancelPurchase parameter has been removed from SwiftUI. Use didFinishPurchase instead. Update your code like this:

@State var paywallPresented = false

var body: some View {
Text("Hello, AdaptyUI!")
.paywall(
isPresented: $paywallPresented,
- paywall: <paywall object>,
- viewConfiguration: <LocalizedViewConfiguration>,
+ paywallConfiguration: <AdaptyUI.PaywallConfiguration>,
didPerformAction: { action in
switch action {
case .close:
paywallPresented = false
default:
// Handle other actions
break
}
},
- didFinishPurchase: { product, profile in paywallPresented = false },
+ didFinishPurchase: { product, purchaseResult in /* handle the result*/ },
didFailPurchase: { product, error in /* handle the error */ },
didFinishRestore: { profile in /* check access level and dismiss */ },
didFailRestore: { error in /* handle the error */ },
didFailRendering: { error in paywallPresented = false }
- didCancelPurchase: { product in /* handle the result*/}

)
}

Update handling of promotional in-app purchases from App Store

Update how you handle promotional in-app purchases from the App Store by removing the defermentCompletion parameter from the AdaptyDelegate method, as shown in the example below:

Swift
final class YourAdaptyDelegateImplementation: AdaptyDelegate {
nonisolated func shouldAddStorePayment(for product: AdaptyDeferredProduct) -> Bool {
// 1a.
// Return `true` to continue the transaction in your app.

// 1b.
// Store the product object and return `false` to defer or cancel the transaction.
false
}

// 2. Continue the deferred purchase later on by passing the product to `makePurchase`
func continueDeferredPurchase() async {
let storedProduct: AdaptyDeferredProduct = // get the product object from the 1b.
do {
try await Adapty.makePurchase(product: storedProduct)
} catch {
// handle the error
}
}
}

Remove getProductsIntroductoryOfferEligibility method

Before Adapty iOS SDK 3.2.0, the product object always included offers, regardless of whether the user was eligible. You had to manually check eligibility before using the offer.

Now, the product object only includes an offer if the user is eligible. This means you no longer need to check eligibility—if an offer is present, the user is eligible.

If you still want to view offers for users who are not eligible, refer to sk1Product and sk2Product.

Update 3d-party integration SDK configuration

Starting with Adapty iOS SDK 3.2.0, we’ve updated the public API for the updateAttribution method. Previously, it accepted a [AnyHashable: Any] dictionary, allowing you to pass attribution objects directly from various services. Now, it requires a [String: any Sendable], so you’ll need to convert attribution objects before passing them.

To ensure integrations work properly with Adapty iOS SDK 3.2.0 and later, update your SDK configurations for the following integrations as described:

Adjust

Update your mobile app code in the following way. The final code example you can find the in the SDK configuration for Adjust integration.

For Adjust version 5.0 or later, use the following:

class AdjustModuleImplementation {
func updateAdjustAttribution() {
Adjust.attribution { attribution in
- guard let attributionDictionary = attribution?.dictionary() else { return }
+ guard let attributionDictionary = attribution?.dictionary()?.toSendableDict() else { return }

Adjust.adid { adid in
guard let adid else { return }

Adapty.updateAttribution(attributionDictionary, source: .adjust, networkUserId: adid) { error in
// handle the error
}
}
}
}
}

+ extension [AnyHashable: Any] {
+ func toSendableDict() -> [String: any Sendable] {
+ var result = [String: any Sendable]()
+
+ for (key, value) in self {
+ guard let stringKey = key as? String else { continue }
+
+ switch value {
+ case let boolValue as Bool:
+ result[stringKey] = boolValue
+ case let stringValue as String:
+ result[stringKey] = stringValue
+ case let stringArrayValue as [String]:
+ result[stringKey] = stringArrayValue
+ case let intValue as Int:
+ result[stringKey] = intValue
+ case let intArrayValue as [Int]:
+ result[stringKey] = intArrayValue
+ case let dictValue as [AnyHashable: Any]:
+ result[stringKey] = dictValue.toSendableDict()
+ default:
+ break
+ }
+ }
+
+ return result
+ }
+}

AppsFlyer

Update your mobile app code in the following way. The final code example you can find the in the SDK configuration for AppsFlyer integration.

class YourAppsFlyerLibDelegateImplementation {
// Find your implementation of AppsFlyerLibDelegate
// and update onConversionDataSuccess method:
func onConversionDataSuccess(_ conversionInfo: [AnyHashable : Any]) {
// It's important to include the network user ID
let networkUserId = AppsFlyerLib.shared().getAppsFlyerUID()

Adapty.updateAttribution(
- conversionInfo,
+ conversionInfo.toSendableDict(),
source: .appsflyer,
networkUserId: networkUserId
)
}
}

+ extension [AnyHashable: Any] {
+ func toSendableDict() -> [String: any Sendable] {
+ var result = [String: any Sendable]()
+
+ for (key, value) in self {
+ guard let stringKey = key as? String else { continue }
+
+ switch value {
+ case let boolValue as Bool:
+ result[stringKey] = boolValue
+ case let stringValue as String:
+ result[stringKey] = stringValue
+ case let stringArrayValue as [String]:
+ result[stringKey] = stringArrayValue
+ case let intValue as Int:
+ result[stringKey] = intValue
+ case let intArrayValue as [Int]:
+ result[stringKey] = intArrayValue
+ case let dictValue as [AnyHashable: Any]:
+ result[stringKey] = dictValue.toSendableDict()
+ default:
+ break
+ }
+ }
+
+ return result
+ }
}

Branch

Update your mobile app code in the following way. The final code example you can find the in the SDK configuration for Branch integration.

class YourBranchImplementation {
func initializeBranch() {
// Pass the attribution you receive from the initializing method of Branch iOS SDK to Adapty.
Branch.getInstance().initSession(launchOptions: launchOptions) { (data, error) in
- if let data = data {
+ if let data = data?.toSendableDict() {
Adapty.updateAttribution(data, source: .branch)
}
}
}
}
}

+ extension [AnyHashable: Any] {
+ func toSendableDict() -> [String: any Sendable] {
+ var result = [String: any Sendable]()
+
+ for (key, value) in self {
+ guard let stringKey = key as? String else { continue }
+
+ switch value {
+ case let boolValue as Bool:
+ result[stringKey] = boolValue
+ case let stringValue as String:
+ result[stringKey] = stringValue
+ case let stringArrayValue as [String]:
+ result[stringKey] = stringArrayValue
+ case let intValue as Int:
+ result[stringKey] = intValue
+ case let intArrayValue as [Int]:
+ result[stringKey] = intArrayValue
+ case let dictValue as [AnyHashable: Any]:
+ result[stringKey] = dictValue.toSendableDict()
+ default:
+ break
+ }
+ }
+
+ return result
+ }
+}