Миграция Adapty Kotlin Multiplatform SDK на v4.1

Adapty Kotlin Multiplatform SDK 4.1 — это первый стабильный релиз ветки 4.x: версия 4.0 вышла только в бета, поэтому если вы используете 3.x, переходите сразу на 4.1. Это руководство охватывает всю миграцию: флоу, введённые в 4.0, и изменения версии 4.1.

Версия 4.x вводит флоу и соответствующим образом переименовывает API пейволов. Новые API работают как с Flow & Paywall Builder, так и со старым Paywall Builder — никаких изменений в настройках на стороне дашборда Adapty не требуется. Помимо этого, версия 4.1 переводит Adapty Attribution в режим opt-in, переименовывает API внешней атрибуции и тип подписки на продукт, а также добавляет поддержку продвигаемых встроенных покупок App Store.

Краткий справочник

v3v4.1
Атрибуция Adapty включена автоматическиотключена по умолчанию — включите с помощью .withAdaptyAttributionEnabled(true)
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)
AdaptyPaywallAdaptyFlow
AdaptyUI.createPaywallView(paywall, ...)AdaptyUI.createFlowView(flow, ...)
AdaptyUI.createNativePaywallView(...) → AdaptyNativePaywallViewAdaptyUI.createNativeFlowView(...) → AdaptyNativeFlowView
AdaptyUIPaywallViewAdaptyUIFlowView
AdaptyUI.presentPaywallView(view) / dismissPaywallView(view)AdaptyUI.presentFlowView(view) / dismissFlowView(view)
AdaptyUI.setPaywallsEventsObserver(observer)AdaptyUI.setFlowsEventsObserver(observer)
AdaptyUI.registerPaywallEventsListener / unregisterPaywallEventsListenerAdaptyUI.registerFlowEventsListener / unregisterFlowEventsListener
AdaptyUIPaywallsEventsObserverAdaptyUIFlowsEventsObserver
AdaptyUIPaywallPlatformView(paywall, ...)AdaptyUIFlowPlatformView(flow, ...)
paywallViewDidPerformAction, paywallViewDidAppear и другие колбэки paywallView...flowViewDidPerformAction, flowViewDidAppear и другие колбэки flowView...
paywallViewDidFailRenderingflowViewDidReceiveError
Adapty.updateAttribution(attribution, source) со строковым параметром sourceAdapty.updateExternalAttribution(attribution, provider) с AdaptyExternalAttributionProvider
провайдер передаётся как строка, например "adjust"AdaptyExternalAttributionProvider, например AdaptyExternalAttributionProvider.ADJUST
AdaptyProfile.appliedAttributionSources: List<String>AdaptyProfile.appliedExternalAttributionProviders: List<AdaptyExternalAttributionProvider>
AdaptyPaywallProductSubscriptionAdaptyProductSubscription
Promoted in-app purchases завершаются автоматически, без возможности перехватить ихOnPromotedPurchaseListener и Adapty.makePromotedPurchase(product) передают завершение покупки вашему приложению

AdaptyPaywallProduct сохраняет своё название — продукты по-прежнему принадлежат флоу, и getPaywallProducts тоже сохраняет название, теперь принимая AdaptyFlow. Методы getFlow и getFlowForDefaultAudience больше не принимают параметр locale — передавайте его в createFlowView. API покупок и профилей (makePurchase, restorePurchases, getProfile, identify, updateProfile) и setFallback сохраняют те же сигнатуры, однако файл резервного пейвола необходимо скачать заново — см. Резервные файлы. Методы онбординга по-прежнему работают, но считаются устаревшими — см. Устаревание API онбординга. Некоторые поведения по умолчанию изменились — см. Изменения поведения по умолчанию.

Установка

Обновите версию и синхронизируйте проект:

[versions]
adapty-kmp = "<the latest SDK version>"

[libraries]
adapty-kmp = { module = "io.adapty:adapty-kmp", version.ref = "adapty-kmp" }
adapty-kmp-ui = { module = "io.adapty:adapty-kmp-ui", version.ref = "adapty-kmp" }

Модуль adapty-kmp-ui нужен только если вы отображаете флоу и пейволы через слой Compose Multiplatform (view.present()). Подробнее об установке см. в разделе Установка Adapty SDK.

Нативные SDK Adapty для обеих платформ обновлены до версии 4.x и подтягиваются автоматически — никаких изменений в сборке не требуется. Минимальная версия iOS остаётся 15.0, это изменение её не затрагивает.

⚠️ Атрибуция Adapty отключена по умолчанию

Warning

Если вы используете Атрибуцию Adapty и обновляетесь до SDK 4.1 без явного подключения, всё сломается без каких-либо предупреждений — установки перестанут фиксироваться, и никаких уведомлений не будет.

В более ранних версиях SDK автоматически регистрировал установки для Атрибуции Adapty. Начиная с версии SDK 4.1, это отключено по умолчанию: SDK не регистрирует установки, слушатель, заданный через setOnInstallationDetailsListener, никогда не срабатывает, а getCurrentInstallationStatus возвращает AdaptyInstallationStatus.Determined.NotAvailable.

Если вы используете Атрибуцию Adapty, включите её при активации SDK:

  val config = AdaptyConfig
      .Builder("PUBLIC_SDK_KEY")
+     .withAdaptyAttributionEnabled(true)
      .build()

  Adapty.activate(configuration = config)

Если вы не используете Adapty Attribution, никаких изменений не требуется.

Получение флоу

getPaywall → getFlow

Возвращаемый тип меняется с AdaptyPaywall на AdaptyFlow, параметр locale переносится из вызова fetch в createFlowView; для кастомных пейволов все локали возвращаются в flow.remoteConfigs:

- Adapty.getPaywall("YOUR_PLACEMENT_ID", locale = "en")
-     .onSuccess { paywall ->
-         // use the paywall
+ Adapty.getFlow("YOUR_PLACEMENT_ID")
+     .onSuccess { flow ->
+         AdaptyUI.createFlowView(flow = flow, locale = "en")
      }
      .onError { error ->
          // handle the error
      }

locale остаётся необязательным параметром в createFlowView: если его не указать, вид отобразится на en или на языке по умолчанию флоу, если в нём нет en. См. Локализации и коды языков.

getPaywallForDefaultAudience переименован аналогичным образом:

- Adapty.getPaywallForDefaultAudience("YOUR_PLACEMENT_ID", locale = "en")
+ Adapty.getFlowForDefaultAudience("YOUR_PLACEMENT_ID")

getPaywallProducts(paywall) → getPaywallProducts(flow)

getPaywallProducts сохраняет своё название, но теперь принимает AdaptyFlow:

- Adapty.getPaywallProducts(paywall)
+ Adapty.getPaywallProducts(flow)
      .onSuccess { products ->
          // use the products
      }

Резервные файлы

Формат резервного файла изменился в SDK v4. Скачайте новый файл в разделе Placements > Fallbacks и добавьте его в приложение.

Модель данных

getFlow возвращает AdaptyFlow вместо AdaptyPaywall, и структура объекта изменилась:

Свойство AdaptyPaywall в v3Свойство AdaptyFlow в v4Действие
remoteConfig: AdaptyRemoteConfig? (одиночный)remoteConfigs: List<AdaptyRemoteConfig>Флоу содержит один Remote Config на каждый настроенный язык. Читайте тот, который соответствует пользователю: flow.remoteConfigs.firstOrNull { it.locale == "en" }.
(новое)paywalls: List<AdaptyFlowPaywall>Каждый элемент — один вариант пейвола во флоу, со своими name, variationId и productIdentifiers. Методы web paywall принимают AdaptyFlowPaywall — см. Методы web paywall.
productIdentifiersперемещеноИдентификаторы продуктов теперь находятся в каждом варианте: flow.paywalls[i].productIdentifiers. Для получения продуктов по-прежнему вызывайте getPaywallProducts(flow).
hasViewConfigurationсохраненоУказывает, содержит ли флоу макет, который AdaptyUI может отобразить. Это свойство отсутствовало в бета-версии 4.0 и вернулось в 4.1 — если вы убрали проверки для беты, можете снова его использовать. Значение false означает, что флоу не содержит макета, поэтому обрабатывайте его только как Remote Config. Также можно вызвать createFlowView и обработать ошибку (см. Отображение флоу).

hasViewConfiguration также присутствует в AdaptyOnboarding и не изменился.

Методы веб-пейвола

openWebPaywall и createWebPaywallUrl сохраняют свои названия, но параметр paywall заменяется параметром flowPaywall, принимающим AdaptyFlowPaywall — одним из вариантов в flow.paywalls. Вместо него по-прежнему можно передать AdaptyPaywallProduct:

- Adapty.openWebPaywall(paywall = paywall)
+ flow.paywalls.firstOrNull()?.let { flowPaywall ->
+     Adapty.openWebPaywall(flowPaywall = flowPaywall)
+ }

Отслеживание просмотров флоу

logShowPaywall → logShowFlow

logShowPaywall переименован в logShowFlow и теперь принимает AdaptyFlow. Событие по-прежнему регистрируется для того же варианта, поэтому существующие метрики воронки и A/B-тестов продолжают работать без изменений в дашборде.

- Adapty.logShowPaywall(paywall)
+ Adapty.logShowFlow(flow)

Как и в v3, вызывать этот метод при отображении флоу или пейволов, отрисованных самим Adapty, не нужно — Adapty отслеживает такие просмотры автоматически.

Отображение флоу

createPaywallView → createFlowView

Переименуйте фабричный метод и передайте AdaptyFlow. Тип возвращаемого значения переименован с AdaptyUIPaywallView на AdaptyUIFlowView, но его методы (present, dismiss) и необязательные параметры (loadTimeout, preloadProducts, customTags, customTimers, customAssets, productPurchaseParams) остались прежними. Добавлен один новый необязательный параметр: locale — он заменяет locale, который раньше передавался в getPaywall. Подробнее см. в разделе Получение флоу.

Note

customTimers по-прежнему существует, но влияет только на пейволы, созданные с помощью устаревшего Paywall Builder. Таймер обратного отсчёта countdown timer во флоу работает по настройкам, заданным во Flow & Paywall Builder, поэтому флоу игнорирует всё, что вы передаёте здесь.

- AdaptyUI.createPaywallView(paywall)
+ AdaptyUI.createFlowView(flow)
      .onSuccess { view ->
          view.present()
      }
      .onError { error ->
          // handle the error
      }

Если вы не используете Compose Multiplatform, нативный фабричный метод переименован так же:

- AdaptyUI.createNativePaywallView(paywall)
+ AdaptyUI.createNativeFlowView(flow)

createFlowView возвращает AdaptyResult.Error, если для флоу не настроен вид, поэтому вы можете убрать проверку hasViewConfiguration из v3 и обработать ошибку вместо неё:

- if (paywall.hasViewConfiguration) {
-     AdaptyUI.createPaywallView(paywall)
-         .onSuccess { view -> view.present() }
- }
+ AdaptyUI.createFlowView(flow)
+     .onSuccess { view -> view.present() }
+     .onError { error ->
+         // the flow has no view configured, or view creation failed
+     }
Note

Представление флоу одноразовое: после вызова dismiss() оно уничтожается, поэтому для повторного показа флоу вызовите createFlowView ещё раз.

Обработка событий

Наблюдатель событий переименован с AdaptyUIPaywallsEventsObserver на AdaptyUIFlowsEventsObserver, а его колбэки меняют префикс paywallView на flowView. Тела существующих обработчиков менять не нужно — достаточно переименовать тип и переопределения:

- AdaptyUI.setPaywallsEventsObserver(object : AdaptyUIPaywallsEventsObserver {
-     override fun paywallViewDidFinishPurchase(
-         view: AdaptyUIPaywallView,
+ AdaptyUI.setFlowsEventsObserver(object : AdaptyUIFlowsEventsObserver {
+     override fun flowViewDidFinishPurchase(
+         view: AdaptyUIFlowView,
          product: AdaptyPaywallProduct,
          purchaseResult: AdaptyPurchaseResult
      ) {
          // custom logic after purchase
      }
  })

Один колбэк также переименован: paywallViewDidFailRendering становится flowViewDidReceiveError. Он срабатывает для тех же ошибок рендеринга, что и раньше, плюс других runtime-ошибок, не связанных с покупкой:

- override fun paywallViewDidFailRendering(view: AdaptyUIPaywallView, error: AdaptyError) {}
+ override fun flowViewDidReceiveError(view: AdaptyUIFlowView, error: AdaptyError) {}

См. Обработка событий флоу и пейвола — полный список колбэков.

Compose platform view

Если вы встраиваете представления через Compose Multiplatform composable, AdaptyUIPaywallPlatformView(paywall, ...) переименовывается в AdaptyUIFlowPlatformView(flow, ...). Колбэки событий сохраняют свои имена onDid..., за исключением onDidFailRendering, который становится onDidReceiveError:

- AdaptyUIPaywallPlatformView(
-     paywall = paywall,
+ AdaptyUIFlowPlatformView(
+     flow = flow,
      onDidFinishPurchase = { view, product, result -> /* ... */ },
  )

Как и в v3, коллбэки, которые вы передаёте здесь (и любой наблюдатель, зарегистрированный через registerFlowEventsListener), выполняются в дополнение к глобальному наблюдателю, а не вместо него — ваш коллбэк наблюдает за событием, но не заменяет глобальное поведение по умолчанию. Учитывайте изменения поведения по умолчанию: например, глобальное поведение по умолчанию больше не закрывает экран после покупки.

Новые API

  • AdaptyUI.setObserverModeResolver(...) с AdaptyUIObserverModeResolver — управляет покупками и восстановлениями, инициированными из флоу, когда SDK работает в режиме Observer. Ранее это было доступно только в нативных iOS- и Android-SDK. См. Отображение флоу в режиме Observer.
  • AdaptyUI.setSystemRequestsHandler(...) с AdaptyUISystemRequestsHandler — зарезервирован для системных запросов из флоу (запросы разрешений ОС и запросы на оценку приложения). Флоу пока не инициируют такие запросы, поэтому регистрировать обработчик не нужно.
  • Новый опциональный колбэк flowViewDidReceiveAnalyticEvent передаёт аналитические события из флоу, начиная с события просмотра экрана для каждого экрана, который открывает пользователь. См. Отслеживание просмотров экранов флоу.
  • AdaptyUI.openWebUrl(url, openIn) и AdaptyUI.requestAppReview() — обеспечивают стандартную обработку OpenUrlAction и handleAppReviewRequest по умолчанию, так что URL и запросы на оценку приложения обрабатываются нативно из коробки. Вызывайте их напрямую только если переопределяете эти значения по умолчанию.
  • AdaptyUIFlowView.locale — возвращает локализацию, с которой было построено представление, чтобы вы могли узнать, какую именно видит пользователь.
  • AdaptyConfig.ServerCluster.CN — новый вариант серверного кластера наряду с DEFAULT и EU, для подключения вашего приложения к серверам Adapty в Китае.

Переименованные API для внешней атрибуции

Начиная с версии SDK 4.1, API для передачи данных атрибуции от внешнего провайдера (Adjust, AppsFlyer, Branch, Tenjin, Apple Ads или кастомного) переименованы в соответствии с нативными SDK, а провайдер изменён со строки на тип. Устаревших псевдонимов нет, поэтому существующие места вызова перестанут компилироваться до тех пор, пока вы их не обновите.

updateAttribution → updateExternalAttribution

Метод переименован, параметр source заменён на provider, и теперь принимает AdaptyExternalAttributionProvider вместо String:

- Adapty.updateAttribution(attribution, "adjust")
+ Adapty.updateExternalAttribution(attribution, AdaptyExternalAttributionProvider.ADJUST)

Данные атрибуции по-прежнему передаются как Map<String, Any>.

Вызов завершается, как только бэкенд принимает данные для асинхронной обработки. Успешный результат не означает, что данные уже применены к профилю.

AdaptyExternalAttributionProvider

Теперь это тип с предопределёнными значениями: APPLE_ADS, ADJUST, APPSFLYER, BRANCH, TENJIN и CUSTOM. Для любого другого провайдера создайте экземпляр из его идентификатора:

AdaptyExternalAttributionProvider("your_provider")

Прямое создание экземпляра также охватывает провайдеров, которые Adapty добавит после выхода этой версии SDK — идентификатор передаётся на бэкенд без изменений, а не схлопывается в неизвестное значение. Пробельные символы по краям обрезаются.

Каждое предопределённое значение оборачивает тот же идентификатор, который вы передавали в updateAttribution ранее: AdaptyExternalAttributionProvider.APPLE_ADS.value — это apple_search_ads, остальные используют собственные имена в нижнем регистре.

AdaptyProfile.appliedAttributionSources → appliedExternalAttributionProviders

Свойство профиля, в котором перечислены провайдеры атрибуции, применённые к профилю, переименовано, и тип его элементов изменился соответственно:

- if (profile.appliedAttributionSources.contains("apple_search_ads")) {
+ if (profile.appliedExternalAttributionProviders.contains(AdaptyExternalAttributionProvider.APPLE_ADS)) {
      // Apple Ads attribution has been applied
  }

AdaptyPaywallProductSubscription → AdaptyProductSubscription

Тип с деталями подписки переименован, так как продвигаемые продукты теперь используют тот же тип. Изменилось только имя — все свойства сохранили свои имена и типы:

- val subscription: AdaptyPaywallProductSubscription? = product.subscription
+ val subscription: AdaptyProductSubscription? = product.subscription

Продвигаемые встроенные покупки в App Store

SDK 4.1 доставляет встроенные покупки, продвигаемые на странице вашего продукта в App Store, в ваше приложение на iOS. В более ранних версиях такая покупка завершалась автоматически, и приложение не могло её перехватить. Начиная с версии 4.1 покупка ожидает вашего кода, поэтому это требует действий, даже если вы никогда не работали с продвигаемыми покупками.

Чтобы поддержать продвигаемые покупки, зарегистрируйте OnPromotedPurchaseListener и завершите покупку, передав продукт в Adapty.makePromotedPurchase. Без зарегистрированного слушателя покупка будет приостановлена, а не завершена: пользователь нажимает Buy на странице App Store, и в вашем приложении ничего не происходит. Зарегистрируйте слушатель как можно раньше — подробнее о тайминге и полном примере см. в разделе Встроенные покупки из App Store.

Выбор конкретного макета

createFlowView, createNativeFlowView и AdaptyUIFlowPlatformView принимают новый необязательный параметр customLayoutId. Передайте его, чтобы отобразить конкретный макет из конфигурации макетов флоу вместо того, который SDK выбирает автоматически в зависимости от типа устройства и размера экрана. Flow & Paywall Builder пока не поддерживает назначение пользовательских идентификаторов макетов, поэтому оставьте этот параметр неустановленным:

AdaptyUI.createFlowView(flow, customLayoutId = "tablet_landscape")

Если ни один макет не соответствует ID, флоу загружается без конфигурации отображения. Параметр необязателен и по умолчанию равен null, поэтому существующие вызовы не затрагиваются.

Изменения поведения по умолчанию

Эти изменения не вызывают ошибок компиляции, поэтому проверяйте их в runtime:

  • Завершение покупки: В v3 дефолтный paywallViewDidFinishPurchase закрывал вью после любого результата покупки, кроме AdaptyPurchaseResult.UserCanceled. В v4 дефолтный flowViewDidFinishPurchase ничего не делает, поэтому флоу остаётся открытым после покупки, пока вы его не закроете — аналогично поведению на iOS. Если вы рассчитывали на автоматическое закрытие, вызовите view.dismiss() самостоятельно после завершения покупки.
  • Системная кнопка «Назад» на Android: В v3 дефолтный paywallViewDidPerformAction закрывал вью как по CloseAction, так и по AndroidSystemBackAction. В v4 дефолтный обработчик реагирует только на CloseAction — системная кнопка «Назад» больше не закрывает флоу автоматически, что соответствует поведению iOS, где флоу нельзя закрыть системным жестом. Дайте пользователям явный способ выйти (кнопка Close или действие on_device_back) или закройте вью самостоятельно в flowViewDidPerformAction.
  • Ошибки вью: В v3 дефолтный paywallViewDidFailRendering ничего не делал. В v4 дефолтный flowViewDidReceiveError закрывает вью — переопределите его, если хотите оставить вью открытым или обработать ошибку иначе.
  • Вью одноразовые: После вызова dismiss() вью уничтожается. Чтобы показать флоу повторно, вызовите createFlowView заново.

Устаревший API онбординга

Устаревший API онбординга в v4 заменён Flow & Paywall Builder. Он по-прежнему работает, но будет удалён в одном из будущих релизов, поэтому планируйте миграцию своих онбордингов на Flow & Paywall Builder.

Устаревшие символы: getOnboarding, getOnboardingForDefaultAudience, AdaptyUI.createOnboardingView, AdaptyUI.createNativeOnboardingView и AdaptyUIOnboardingsEventsObserver.