Миграция iOS SDK Adapty на версию 3.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 и Google Analytics, Mixpanel, OneSignal, Pushwoosh.
  7. Обновите реализацию Observer mode.

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

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

Переименование метода 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 встроенных покупок из App Store

Обновите обработку promotional встроенных покупок из 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 mode

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

Не забывайте записывать транзакцию с помощью метода 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
+ }