Миграция Adapty iOS SDK на v3.3

Adapty SDK 3.3.0 — это мажорный релиз, который принёс ряд улучшений, однако для перехода на него могут потребоваться некоторые шаги по миграции.

  1. Переименуйте Adapty.Configuration в AdaptyConfiguration.
  2. Переименуйте метод getViewConfiguration в getPaywallConfiguration.
  3. Удалите параметры didCancelPurchase и paywall из SwiftUI, а параметр viewConfiguration переименуйте в paywallConfiguration.
  4. Обновите обработку promotional встроенных покупок из App Store, удалив параметр defermentCompletion из метода AdaptyDelegate.
  5. Удалите метод getProductsIntroductoryOfferEligibility.
  6. Обновите конфигурации интеграций для Adjust, AirBridge, Amplitude, AppMetrica, Appsflyer, Branch, Facebook Ads, Firebase and Google Analytics, Mixpanel, OneSignal, Pushwoosh.
  7. Обновите реализацию режима Observer.

Переименование Adapty.Configuration в AdaptyConfiguration

Обновите код активации Adapty iOS SDK следующим образом:

Переименование метода getViewConfiguration в getPaywallConfiguration

Обновите название метода для получения viewConfiguration пейвола:


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
}

Подробнее о методе читайте в разделе Получение конфигурации представления пейвола, созданного в Paywall Builder.

Изменения параметров в SwiftUI

В SwiftUI внесены следующие изменения:

  1. Параметр didCancelPurchase удалён. Используйте вместо него didFinishPurchase.
  2. Метод .paywall() больше не принимает объект пейвола.
  3. Параметр paywallConfiguration заменил параметр viewConfiguration.

Обновите свой код следующим образом:

@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*/}

      )
}

Обновление обработки promotional in-app purchases из App Store

Обновите обработку promotional in-app purchases из App Store, удалив параметр defermentCompletion из метода AdaptyDelegate, как показано в примере ниже:

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
        }
    }
}

Удаление метода getProductsIntroductoryOfferEligibility

До версии Adapty iOS SDK 3.3.0 объект продукта всегда включал офферы вне зависимости от того, имел ли пользователь право на их использование. Вам приходилось вручную проверять право доступа перед использованием оффера.

Теперь объект продукта включает оффер только в том случае, если пользователь имеет на него право. Это означает, что проверка права доступа больше не нужна — если оффер присутствует, пользователь имеет на него право.

Если вы всё же хотите просматривать офферы для пользователей, не имеющих права доступа, обращайтесь к sk1Product и sk2Product.

Обновление конфигурации SDK для сторонних интеграций

Начиная с Adapty iOS SDK 3.3.0, мы обновили публичный API метода updateAttribution. Ранее он принимал словарь [AnyHashable: Any], позволяя передавать объекты атрибуции напрямую из различных сервисов. Теперь требуется [String: any Sendable], поэтому перед передачей необходимо конвертировать объекты атрибуции.

Чтобы интеграции корректно работали с Adapty iOS SDK 3.3.0 и выше, обновите конфигурации SDK для перечисленных ниже интеграций согласно соответствующим разделам.

Adjust

Обновите код мобильного приложения, как показано ниже. Полный пример кода смотрите в разделе настройка SDK для интеграции с Adjust.

AirBridge

Обновите код мобильного приложения, как показано ниже. Полный пример кода смотрите в разделе Настройка SDK для интеграции AirBridge.

 import AirBridge

- let builder = AdaptyProfileParameters.Builder()
-             .with(airbridgeDeviceId: AirBridge.deviceUUID())
-
- Adapty.updateProfile(params: builder.build())

+ do {
+     try await Adapty.setIntegrationIdentifier(
+         key: "airbridge_device_id", 
+         value: AirBridge.deviceUUID()
+     )
+ } catch {
+     // handle the error
+ }

Amplitude

Обновите код мобильного приложения, как показано ниже. Полный пример кода смотрите в разделе настройка SDK для интеграции с Amplitude.

 import Amplitude 

- let builder = AdaptyProfileParameters.Builder()
-             .with(amplitudeUserId: Amplitude.instance().userId)
-             .with(amplitudeDeviceId: Amplitude.instance().deviceId)
-
- Adapty.updateProfile(params: builder.build())

+ do {
+     try await Adapty.setIntegrationIdentifier(
+         key: "amplitude_user_id", 
+         value: Amplitude.instance().userId
+     )
+     try await Adapty.setIntegrationIdentifier(
+         key: "amplitude_device_id", 
+         value: Amplitude.instance().deviceId
+     )
+ } catch {
+     // handle the error
+ }

AppMetrica

Обновите код мобильного приложения, как показано ниже. Полный пример кода смотрите в разделе Настройка SDK для интеграции с AppMetrica.

 import AppMetricaCore
        
- if let deviceID = AppMetrica.deviceID {
-   let builder = AdaptyProfileParameters.Builder()
-     .with(appmetricaDeviceId: deviceID)
-     .with(appmetricaProfileId: "YOUR_ADAPTY_CUSTOMER_USER_ID")
-
-   Adapty.updateProfile(params: builder.build())
- }

+ if let deviceID = AppMetrica.deviceID {
+     do {
+         try await Adapty.setIntegrationIdentifier(
+             key: "appmetrica_device_id", 
+             value: deviceID
+         )
+         try await Adapty.setIntegrationIdentifier(
+             key: "appmetrica_profile_id", 
+             value: "YOUR_ADAPTY_CUSTOMER_USER_ID"
+         )
+     } catch {
+         // handle the error
+     }
+ }

AppsFlyer

Обновите код мобильного приложения, как показано ниже. Полный пример кода можно найти в разделе Настройка SDK для интеграции с AppsFlyer.

class YourAppsFlyerLibDelegateImplementation {
    // Find your implementation of AppsFlyerLibDelegate 
    // and update onConversionDataSuccess method:
     func onConversionDataSuccess(_ conversionInfo: [AnyHashable : Any]) {
         let uid = AppsFlyerLib.shared().getAppsFlyerUID()

-        Adapty.updateAttribution(
-           conversionInfo.toSendableDict(),
-            source: .appsflyer,
-            networkUserId: uid
-        )
+        Adapty.setIntegrationIdentifier(key: "appsflyer_id", value: uid)
+        Adapty.updateAttribution(conversionInfo, source: "appsflyer")
    }
}

Branch

Обновите код вашего мобильного приложения, как показано ниже. Полный пример кода смотрите в разделе Настройка SDK для интеграции с Branch.

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?.toSendableDict() {
-                Adapty.updateAttribution(data, source: .branch)
-           }
+           if let data {
+               Adapty.updateAttribution(data, source: "branch")
+           }
        }
    }
}

Facebook Ads

Обновите код мобильного приложения, как показано ниже. Полный пример кода смотрите в разделе настройка SDK для интеграции с Facebook Ads.

 import FacebookCore

- let builder = AdaptyProfileParameters.Builder()
-     .with(facebookAnonymousId: AppEvents.shared.anonymousID)
-
- do {
-     try Adapty.updateProfile(params: builder.build())
- } catch {
-     // handle the error
- }

+ do {
+     try await Adapty.setIntegrationIdentifier(
+         key: "facebook_anonymous_id", 
+         value: AppEvents.shared.anonymousID
+     )
+ } catch {
+     // handle the error
+ }

Firebase и Google Analytics

Обновите код мобильного приложения, как показано ниже. Полный пример кода смотрите в разделе Настройка SDK для интеграции с Firebase и Google Analytics.

 import FirebaseCore
 import FirebaseAnalytics

 FirebaseApp.configure()
        
- if let appInstanceId = Analytics.appInstanceID() {            
-     let builder = AdaptyProfileParameters.Builder()
-         .with(firebaseAppInstanceId: appInstanceId)
            
-     Adapty.updateProfile(params: builder.build()) { error in
-         // handle error
-     }
- }

+ if let appInstanceId = Analytics.appInstanceID() {            
+     do {
+         try await Adapty.setIntegrationIdentifier(
+             key: "firebase_app_instance_id", 
+             value: appInstanceId
+         )
+     } catch {
+         // handle the error
+     }
+ }

Mixpanel

Обновите код мобильного приложения, как показано ниже. Полный пример кода можно найти в разделе Настройка SDK для интеграции с Mixpanel.

 import Mixpanel

- let builder = AdaptyProfileParameters.Builder()
-             .with(mixpanelUserId: Mixpanel.mainInstance().distinctId)
-
- do {
-     try await Adapty.updateProfile(params: builder.build())
- } catch {
-     // handle the error
- }

+ do {
+     try await Adapty.setIntegrationIdentifier(
+         key: "mixpanel_user_id", 
+         value: Mixpanel.mainInstance().distinctId
+     )
+ } catch {
+     // handle the error
+ }

OneSignal

Обновите код мобильного приложения, как показано ниже. Полный пример кода можно найти в разделе Настройка SDK для интеграции с OneSignal.

 // PlayerID (pre-v5 OneSignal SDK)
 // in your OSSubscriptionObserver implementation
 func onOSSubscriptionChanged(_ stateChanges: OSSubscriptionStateChanges) {
     if let playerId = stateChanges.to.userId {
-         let params = AdaptyProfileParameters.Builder()
-             .with(oneSignalPlayerId: playerId)
-             .build()
-
-         Adapty.updateProfile(params:params) { error in
-             // check error
-         }
+         Task {
+             try await Adapty.setIntegrationIdentifier(
+                 key: "one_signal_player_id", 
+                 value: playerId
+             )
+         }
     }
 }

 // SubscriptionID (v5+ OneSignal SDK)
 OneSignal.Notifications.requestPermission({ accepted in
-     let id = OneSignal.User.pushSubscription.id
-
-     let builder = AdaptyProfileParameters.Builder()
-         .with(oneSignalSubscriptionId: id)
-
-     Adapty.updateProfile(params: builder.build())
+     Task {
+         try await Adapty.setIntegrationIdentifier(
+             key: "one_signal_subscription_id", 
+             value: OneSignal.User.pushSubscription.id
+         )
+     }
 }, fallbackToSettings: true)

Pushwoosh

Обновите код мобильного приложения, как показано ниже. Полный пример кода смотрите в разделе Настройка SDK для интеграции с Pushwoosh.

- let params = AdaptyProfileParameters.Builder()
-     .with(pushwooshHWID: Pushwoosh.sharedInstance().getHWID())
-     .build()
-
- Adapty.updateProfile(params: params) { error in
-     // handle the error
- }

+ do {
+     try await Adapty.setIntegrationIdentifier(
+         key: "pushwoosh_hwid", 
+         value: Pushwoosh.sharedInstance().getHWID()
+     )
+ } catch {
+     // handle the error
+ }

Обновление реализации режима Observer

Обновите способ привязки пейволов к транзакциям. Раньше для назначения variationId использовался метод setVariationId. Теперь можно передавать variationId напрямую при записи транзакции с помощью нового метода reportTransaction. Ознакомьтесь с итоговым примером кода в разделе Привязка пейволов к транзакциям покупок в режиме Observer.

Не забудьте зафиксировать транзакцию с помощью метода reportTransaction. Если пропустить этот шаг, Adapty не распознает транзакцию, не предоставит уровни доступа, не включит её в аналитику и не передаст в интеграции. Этот шаг обязателен!

- let variationId = paywall.variationId
-
- // There are two overloads: for StoreKit 1 and StoreKit 2
- Adapty.setVariationId(variationId, forPurchasedTransaction: transaction) { error in
-     if error == nil {
-         // successful binding
-     }
- }

+ do {
+     // every time when calling transaction.finish()
+     try await Adapty.reportTransaction(transaction, withVariationId: <YOUR_PAYWALL_VARIATION_ID>)
+ } catch {
+     // handle the error
+ }