Миграция iOS SDK Adapty до версии v4.0
Adapty iOS SDK 4.0 (beta) вводит флоу и переименовывает paywall API соответственно. Новые API работают как с новым Flow Builder, так и с существующим Paywall Builder — никаких изменений в настройках дашборда Adapty не требуется.
Краткий справочник
| v3 | v4 |
|---|---|
Adapty.getPaywall(placementId:locale:) | Adapty.getFlow(placementId:) |
AdaptyUI.getPaywallConfiguration(forPaywall:) | AdaptyUI.getFlowConfiguration(forFlow:locale:) |
Adapty.getPaywallProducts(paywall:) | Adapty.getPaywallProducts(flow:) |
Adapty.logShowPaywall(_:) | Adapty.logShowFlow(_:) |
AdaptyPaywallController | AdaptyFlowController |
AdaptyPaywallControllerDelegate | AdaptyFlowControllerDelegate |
AdaptyUI.paywallController(with:delegate:) | AdaptyUI.flowController(with:delegate:) |
.paywall() (SwiftUI modifier) | .flow() |
AdaptyPaywallView | AdaptyFlowView |
didFailRenderingWith: / didFailRendering: | didReceiveError: |
Adapty.updateAttribution(_:source:) (source: String) | Adapty.updateAttribution(_:source:) (source: AdaptyAttributionSource) |
Adapty.setIntegrationIdentifier(key:value:) | Adapty.setIntegrationIdentifier(_:) (AdaptyIntegrationIdentifier) |
Минимальная версия iOS
Adapty iOS SDK 4.0 повышает минимальный deployment target с iOS 13.0 до iOS 15.0. Перед обновлением установите iOS Deployment Target вашего проекта на версию 15.0 или выше.
Установка: CocoaPods больше не поддерживается
Adapty iOS SDK 4.0 отказывается от поддержки CocoaPods. Устанавливайте SDK через Swift Package Manager.
Если ваш проект всё ещё использует CocoaPods, удалите поды Adapty и AdaptyUI из Podfile, выполните pod install, чтобы убрать их, а затем добавьте пакет в Xcode через File → Add Package Dependency, указав https://github.com/adaptyteam/AdaptySDK-iOS.git.
Удалённые API
Adapty.getPaywallProductsWithoutDeterminingOffer(paywall:)— удалён. Все продукты теперь включают информацию об офере, поэтому отдельный проход для определения доступности больше не нужен.AdaptyPaywallProductWithoutDeterminingOffer— удалён. Коллбэки, которые раньше передавали этот тип (например,didSelectProduct), теперь передаютAdaptyPaywallProduct.
Временное удаление поддержки promoted in-app purchases из App Store
В рамках миграции на StoreKit 2 Adapty iOS SDK 4.0 убирает поддержку promoted in-app purchases из App Store. Метод делегата shouldAddStorePayment(for:) и тип AdaptyDeferredProduct, который он принимает, недоступны в версии 4.0.
Это временное ограничение — поддержка promoted in-app purchases вернётся в одном из следующих релизов 4.x. Если ваше приложение использует promoted in-app purchases, оставайтесь на iOS SDK 3.x до её возвращения.
Получение пейволов
getPaywall + getPaywallConfiguration → getFlow + getFlowConfiguration
Возвращаемые типы меняются с AdaptyPaywall / AdaptyUI.PaywallConfiguration на AdaptyFlow / AdaptyUI.FlowConfiguration. Параметр locale переносится из вызова получения данных в getFlowConfiguration:
- let paywall = try await Adapty.getPaywall(placementId: "YOUR_PLACEMENT_ID", locale: "en")
- let paywallConfiguration = try await AdaptyUI.getPaywallConfiguration(forPaywall: paywall)
+ let flow = try await Adapty.getFlow(placementId: "YOUR_PLACEMENT_ID")
+ let flowConfiguration = try await AdaptyUI.getFlowConfiguration(forFlow: flow, locale: "en")
getPaywallProducts(paywall:) → getPaywallProducts(flow:)
getPaywallProducts теперь принимает AdaptyFlow, возвращаемый Adapty.getFlow:
- let products = try await Adapty.getPaywallProducts(paywall: paywall)
+ let products = try await Adapty.getPaywallProducts(flow: flow)
Отслеживание просмотров пейвола
logShowPaywall(:) → logShowFlow(:)
logShowPaywall переименован в logShowFlow и теперь принимает AdaptyFlow вместо AdaptyPaywall. Событие по-прежнему записывается к тому же варианту, поэтому существующие метрики воронки и A/B-тестов продолжают работать без изменений в дашборде.
- try await Adapty.logShowPaywall(paywall)
+ try await Adapty.logShowFlow(flow)
Как и в v3, вам не нужно вызывать этот метод при отображении флоу или пейволов, отрисованных с помощью Flow Builder или Paywall Builder — Adapty отслеживает эти просмотры автоматически.
UIKit
AdaptyPaywallController → AdaptyFlowController
Переименуйте тип контроллера и фабричный метод:
- let controller = try AdaptyUI.paywallController(
- with: paywallConfiguration,
- delegate: self
- )
+ let controller = try AdaptyUI.flowController(
+ with: flowConfiguration,
+ delegate: self
+ )
AdaptyPaywallControllerDelegate → AdaptyFlowControllerDelegate
Переименуйте протокол и обновите каждую сигнатуру метода. Обратите внимание, что didSelectProduct теперь принимает AdaptyPaywallProduct вместо удалённого AdaptyPaywallProductWithoutDeterminingOffer.
- class YourClass: AdaptyPaywallControllerDelegate {
+ class YourClass: AdaptyFlowControllerDelegate {
- func paywallControllerDidAppear(_ controller: AdaptyPaywallController) { }
+ func flowControllerDidAppear(_ controller: AdaptyFlowController) { }
- func paywallControllerDidDisappear(_ controller: AdaptyPaywallController) { }
+ func flowControllerDidDisappear(_ controller: AdaptyFlowController) { }
- func paywallController(_ controller: AdaptyPaywallController,
- didPerform action: AdaptyUI.Action) { }
+ func flowController(_ controller: AdaptyFlowController,
+ didPerform action: AdaptyUI.Action) { }
- func paywallController(_ controller: AdaptyPaywallController,
- didSelectProduct product: AdaptyPaywallProductWithoutDeterminingOffer) { }
+ func flowController(_ controller: AdaptyFlowController,
+ didSelectProduct product: AdaptyPaywallProduct) { }
- func paywallController(_ controller: AdaptyPaywallController,
- didStartPurchase product: AdaptyPaywallProduct) { }
+ func flowController(_ controller: AdaptyFlowController,
+ didStartPurchase product: AdaptyPaywallProduct) { }
- func paywallController(_ controller: AdaptyPaywallController,
- didFinishPurchase product: AdaptyPaywallProduct,
- purchaseResult: AdaptyPurchaseResult) { }
+ func flowController(_ controller: AdaptyFlowController,
+ didFinishPurchase product: AdaptyPaywallProduct,
+ purchaseResult: AdaptyPurchaseResult) { }
- func paywallController(_ controller: AdaptyPaywallController,
- didFailPurchase product: AdaptyPaywallProduct,
- error: AdaptyError) { }
+ func flowController(_ controller: AdaptyFlowController,
+ didFailPurchase product: AdaptyPaywallProduct,
+ error: AdaptyError) { }
- func paywallControllerDidStartRestore(_ controller: AdaptyPaywallController) { }
+ func flowControllerDidStartRestore(_ controller: AdaptyFlowController) { }
- func paywallController(_ controller: AdaptyPaywallController,
- didFinishRestoreWith profile: AdaptyProfile) { }
+ func flowController(_ controller: AdaptyFlowController,
+ didFinishRestoreWith profile: AdaptyProfile) { }
- func paywallController(_ controller: AdaptyPaywallController,
- didFailRestoreWith error: AdaptyError) { }
+ func flowController(_ controller: AdaptyFlowController,
+ didFailRestoreWith error: AdaptyError) { }
- func paywallController(_ controller: AdaptyPaywallController,
- didFailRenderingWith error: AdaptyUIError) { }
+ func flowController(_ controller: AdaptyFlowController,
+ didReceiveError error: AdaptyUIError) { }
- func paywallController(_ controller: AdaptyPaywallController,
- didFailLoadingProductsWith error: AdaptyError) -> Bool { }
+ func flowController(_ controller: AdaptyFlowController,
+ didFailLoadingProductsWith error: AdaptyError) -> Bool { }
- func paywallController(_ controller: AdaptyPaywallController,
- didPartiallyLoadProducts failedIds: [String]) { }
+ func flowController(_ controller: AdaptyFlowController,
+ didPartiallyLoadProducts failedIds: [String]) { }
- func paywallController(_ controller: AdaptyPaywallController,
- didFinishWebPaymentNavigation product: AdaptyPaywallProduct?,
- error: AdaptyError?) { }
+ func flowController(_ controller: AdaptyFlowController,
+ didFinishWebPaymentNavigation product: AdaptyPaywallProduct?,
+ error: AdaptyError?) { }
}
SwiftUI
Модификатор .paywall() → .flow()
Переименуйте модификатор и обновите имя параметра конфигурации:
@State var flowPresented = false // переименуйте как угодно — имя переменной на ваш выбор
var body: some View {
Text("Hello, AdaptyUI!")
- .paywall(
+ .flow(
isPresented: $flowPresented,
- paywallConfiguration: paywallConfiguration,
+ flowConfiguration: flowConfiguration,
didFailPurchase: { product, error in /* обработать ошибку */ },
didFinishRestore: { profile in /* проверить уровень доступа и закрыть */ },
didFailRestore: { error in /* обработать ошибку */ },
- didFailRendering: { error in flowPresented = false }
+ didReceiveError: { error in flowPresented = false }
)
}
Переименованный колбэк срабатывает при тех же ошибках рендеринга, что и didFailRendering, плюс при новых ошибках выполнения из скрипта флоу (JavaScript-исключения с кодом AdaptyUIError 4105 — .jsException). Менять код в теле обработчика не нужно — достаточно переименовать параметр.
AdaptyPaywallView → AdaptyFlowView
Переименуйте вью, обновите параметр конфигурации и обновите замыкание didSelectProduct — теперь оно получает AdaptyPaywallProduct вместо удалённого AdaptyPaywallProductWithoutDeterminingOffer:
- AdaptyPaywallView(
- paywallConfiguration: paywallConfiguration,
- didSelectProduct: { product: AdaptyPaywallProductWithoutDeterminingOffer in /* handle */ },
+ AdaptyFlowView(
+ flowConfiguration: flowConfiguration,
+ didSelectProduct: { product: AdaptyPaywallProduct in /* handle */ },
didFailPurchase: { product, error in /* handle the error */ },
didFinishRestore: { profile in /* check access level and dismiss */ },
didFailRestore: { error in /* handle the error */ },
- didFailRendering: { error in /* handle the error */ }
+ didReceiveError: { error in /* handle the error */ }
)
Пользовательские ресурсы AdaptyUI
AdaptyUICustomVideoAsset
Два изменения затрагивают все существующие места вызова:
.playerтеперь принимаетAVPlayerвместоAVQueuePlayer.- В каждый case добавлен завершающий параметр
resolution: CGSize?. Передайтеnil, чтобы сохранить текущее поведение, или передайте фактический размер в пикселях, чтобы плеер мог зарезервировать место в лэйауте (соотношение сторон =width / height) до загрузки видео.
- case file(url: URL, preview: AdaptyUICustomImageAsset?)
- case remote(url: URL, preview: AdaptyUICustomImageAsset?)
- case player(item: AVPlayerItem, player: AVQueuePlayer, preview: AdaptyUICustomImageAsset?)
+ case file(url: URL, preview: AdaptyUICustomImageAsset?, resolution: CGSize?)
+ case remote(url: URL, preview: AdaptyUICustomImageAsset?, resolution: CGSize?)
+ case player(item: AVPlayerItem, player: AVPlayer, preview: AdaptyUICustomImageAsset?, resolution: CGSize?)
Идентификаторы атрибуции и интеграций
updateAttribution(_:source:)
Параметр source изменился с типа String на новый тип AdaptyAttributionSource, а вложенный ранее AdaptyProfile.AttributionSource переименован в AdaptyAttributionSource на верхнем уровне. Используйте один из предопределённых источников или передайте строковый литерал для любого другого источника — AdaptyAttributionSource поддерживает ExpressibleByStringLiteral, поэтому существующие вызовы со строковыми литералами продолжат компилироваться.
- try await Adapty.updateAttribution(attribution, source: "adjust")
+ try await Adapty.updateAttribution(attribution, source: .adjust)
Предопределённые источники: .appleAds, .adjust, .appsflyer, .branch, .tenjin. Если источник хранится в переменной типа String, оберните его: AdaptyAttributionSource(rawValue: yourSource).
setIntegrationIdentifier(_:)
setIntegrationIdentifier(key:value:) заменён вариативным методом, который принимает одно или несколько значений AdaptyIntegrationIdentifier. Используйте предопределённые фабричные методы вместо строковых ключей:
- try await Adapty.setIntegrationIdentifier(key: "appsflyer_id", value: uid)
+ try await Adapty.setIntegrationIdentifier(.appsflyerId(uid))
Можно задать сразу несколько идентификаторов в одном вызове:
try await Adapty.setIntegrationIdentifier(
.appsflyerId(uid),
.adjustDeviceId(adid)
)
Замените каждую старую строку ключа соответствующим фабричным методом:
| ключ v3 | фабрика v4 |
|---|---|
"adjust_device_id" | .adjustDeviceId(_:) |
"airbridge_device_id" | .airbridgeDeviceId(_:) |
"amplitude_user_id" | .amplitudeUserId(_:) |
"amplitude_device_id" | .amplitudeDeviceId(_:) |
"appmetrica_device_id" | .appmetricaDeviceId(_:) |
"appmetrica_profile_id" | .appmetricaProfileId(_:) |
"appsflyer_id" | .appsflyerId(_:) |
"branch_id" | .branchId(_:) |
"facebook_anonymous_id" | .facebookAnonymousId(_:) |
"firebase_app_instance_id" | .firebaseAppInstanceId(_:) |
"mixpanel_user_id" | .mixpanelUserId(_:) |
"one_signal_subscription_id" | .oneSignalSubscriptionId(_:) |
"one_signal_player_id" | .oneSignalPlayerId(_:) |
"posthog_distinct_user_id" | .posthogDistinctUserId(_:) |
"pushwoosh_hwid" | .pushwooshHWID(_:) |
"tenjin_analytics_installation_id" | .tenjinAnalyticsInstallationId(_:) |