Обработка событий флоу и пейвола - iOS
Этот гайд охватывает обработку событий для покупок, восстановлений, выбора продукта и рендеринга пейвола. Вам также необходимо реализовать обработку кнопок (закрытие пейвола, открытие ссылок и т. д.). Подробнее — в нашем гайде по обработке действий кнопок.
Флоу и пейволы не требуют дополнительного кода для совершения и восстановления покупок. Однако они генерируют события, на которые ваше приложение может реагировать. К таким событиям относятся нажатия кнопок (кнопки закрытия, URL, выбор продуктов и т. д.), а также уведомления о действиях, связанных с покупками. Узнайте ниже, как реагировать на эти события.
Хотите увидеть реальный пример интеграции Adapty SDK в мобильное приложение? Ознакомьтесь с нашими примерами приложений, которые демонстрируют полную настройку, включая отображение пейволов, совершение покупок и другие базовые функции.
Обработка событий в SwiftUI
Для управления и отслеживания процессов на экране флоу или пейвола в вашем мобильном приложении используйте модификатор .flow в SwiftUI:
@State var flowPresented = false
var body: some View {
Text("Hello, AdaptyUI!")
.flow(
isPresented: $flowPresented,
flowConfiguration: flowConfiguration,
didPerformAction: { action in
switch action {
case .close:
flowPresented = false
case let .openURL(url):
// handle opening the URL (incl. for terms and privacy)
default:
// handle other actions
}
},
didSelectProduct: { product in /* Handle the event */ },
didStartPurchase: { product in /* Handle the event */ },
didFailPurchase: { product, error in /* handle the error */ },
didStartRestore: { /* Handle the event */ },
didFinishRestore: { profile in /* check access level and dismiss */ },
didFailRestore: { error in /* handle the error */ },
didReceiveError: { error in
flowPresented = false
},
didFailLoadingProducts: { error in
// Return `true` to retry loading
return false
}
)
}Вы можете регистрировать только те параметры замыкания, которые вам нужны, и опускать те, которые не нужны.
| Параметр | Обязательный | Описание |
|---|---|---|
| isPresented | обязательный | Привязка, управляющая отображением экрана флоу или пейвола. |
| flowConfiguration | обязательный | Объект AdaptyUI.FlowConfiguration, содержащий визуальные данные флоу или пейвола. Подробнее см. в разделе Получение флоу и пейволов. |
| didFailPurchase | обязательный | Вызывается при ошибке Adapty.makePurchase(). |
| didFinishRestore | обязательный | Вызывается при успешном завершении Adapty.restorePurchases(). |
| didFailRestore | обязательный | Вызывается при ошибке Adapty.restorePurchases(). |
| didReceiveError | обязательный | Вызывается, когда флоу сталкивается с ошибкой рендеринга или ошибкой времени выполнения из скрипта флоу (например, исключение JavaScript, код AdaptyUIError 4105). В случае ошибки рендеринга обратитесь в поддержку Adapty. |
| placeholderBuilder | необязательный | Функция для рендеринга заглушки, пока флоу или пейвол загружается. По умолчанию используется ProgressView. |
| fullScreen | необязательный | Определяет, отображается ли флоу или пейвол в полноэкранном режиме или как шит. По умолчанию true. |
| didAppear | необязательный | Вызывается, когда экран флоу или пейвола появляется на экране. |
| didDisappear | необязательный | Вызывается, когда экран флоу или пейвола закрывается. |
| didPerformAction | необязательный | Вызывается при нажатии кнопки пользователем. Предопределены два ID действий: close и openURL; остальные задаются в билдере как пользовательские. |
| didSelectProduct | необязательный | Вызывается, когда продукт выбирается для покупки пользователем или системой. |
| didStartPurchase | необязательный | Вызывается, когда пользователь начинает процесс покупки. |
| didFinishPurchase | необязательный | Вызывается при успешном завершении Adapty.makePurchase(). |
| didFinishWebPaymentNavigation | необязательный | Вызывается по завершении навигации веб-оплаты. |
| didStartRestore | необязательный | Вызывается, когда пользователь начинает процесс восстановления покупок. |
| didFailLoadingProducts | необязательный | Вызывается при ошибках загрузки продуктов. Верните true, чтобы повторить загрузку. |
| didPartiallyLoadProducts | необязательный | Вызывается при частичной загрузке продуктов. |
| showAlertItem | необязательный | Привязка, управляющая отображением элементов оповещения поверх флоу или пейвола. |
| showAlertBuilder | необязательный | Функция для рендеринга представления оповещения. |
Обработка событий в UIKit
Для UIKit-приложений события обрабатываются через протокол AdaptyFlowControllerDelegate. Подробнее о настройке AdaptyFlowController с AdaptyFlowControllerDelegate см. в разделе Отображение флоу и пейволов — iOS.
Протокол объявляет 13 методов. Три из них не имеют реализации по умолчанию и должны быть реализованы при соответствии протоколу: didFailPurchase, didFinishRestoreWith и didFailRestoreWith. Остальные методы имеют реализацию по умолчанию (ничего не делающую) и могут быть переопределены при необходимости кастомного поведения. Методы ниже сгруппированы по назначению.
Жизненный цикл
func flowControllerDidAppear(_ controller: AdaptyFlowController) { }
func flowControllerDidDisappear(_ controller: AdaptyFlowController) { }Эти методы срабатывают при появлении и закрытии флоу или пейвола.
Действия пользователя
func flowController(
_ controller: AdaptyFlowController,
didPerform action: AdaptyUI.Action
) { }Варианты AdaptyUI.Action:
.close— по умолчанию закрывает контроллер. Переопределите, если нужно оставить контроллер на экране или выполнить дополнительную очистку..openURL(url:)— по умолчанию открывает URL черезUIApplication.shared.open(...)..custom(id:)— срабатывает для кнопок, которым в билдере назначен пользовательский идентификатор действия.
Выбор продукта
func flowController(
_ controller: AdaptyFlowController,
didSelectProduct product: AdaptyPaywallProduct
) { }Вызывается, когда пользователь или система выбирают продукт для покупки. Продукт содержит полную информацию об офере (в v4 совместимость определяется автоматически — отдельного типа AdaptyPaywallProductWithoutDeterminingOffer больше нет).
События покупки
func flowController(
_ controller: AdaptyFlowController,
didStartPurchase product: AdaptyPaywallProduct
) { }
func flowController(
_ controller: AdaptyFlowController,
didFinishPurchase product: AdaptyPaywallProduct,
purchaseResult: AdaptyPurchaseResult
) {
// Default: dismiss the controller unless the purchase was cancelled.
}
func flowController(
_ controller: AdaptyFlowController,
didFailPurchase product: AdaptyPaywallProduct,
error: AdaptyError
) { }didFailPurchase — единственное событие покупки без реализации по умолчанию. didFinishPurchase по умолчанию закрывает контроллер при успешной покупке; переопределяйте только если нужна собственная логика после покупки.
События восстановления покупок
func flowControllerDidStartRestore(_ controller: AdaptyFlowController) { }
func flowController(
_ controller: AdaptyFlowController,
didFinishRestoreWith profile: AdaptyProfile
) { }
func flowController(
_ controller: AdaptyFlowController,
didFailRestoreWith error: AdaptyError
) { }didFinishRestoreWith и didFailRestoreWith не имеют реализаций по умолчанию. Перед закрытием контроллера убедитесь, что возвращённый AdaptyProfile содержит нужный уровень доступа.
Ошибки флоу и ошибки загрузки продуктов
func flowController(
_ controller: AdaptyFlowController,
didReceiveError error: AdaptyUIError
) { }
func flowController(
_ controller: AdaptyFlowController,
didFailLoadingProductsWith error: AdaptyError
) -> Bool {
// Return `true` to retry product loading; default returns `false`.
return false
}
func flowController(
_ controller: AdaptyFlowController,
didPartiallyLoadProducts failedIds: [String]
) { }didReceiveError срабатывает при ошибках рендеринга и при ошибках выполнения из скрипта флоу (исключения JavaScript, AdaptyUIError код 4105). При ошибках рендеринга обратитесь в поддержку Adapty. При ошибках загрузки верните true из didFailLoadingProductsWith, чтобы повторить попытку — это удобно при временных сбоях сети.
Навигация при веб-оплате
func flowController(
_ controller: AdaptyFlowController,
didFinishWebPaymentNavigation product: AdaptyPaywallProduct?,
error: AdaptyError?
) { }Вызывается после завершения навигации при веб-оплате — как успешной, так и неуспешной.
Этот гайд охватывает обработку событий для покупок, восстановлений, выбора продуктов и отображения пейвола. Вам также необходимо реализовать обработку кнопок (закрытие пейвола, открытие ссылок и т. д.). Подробнее — в гайде по обработке действий кнопок.
Пейволы, настроенные с помощью Paywall Builder, не требуют дополнительного кода для совершения и восстановления покупок. Однако они генерируют события, на которые ваше приложение может реагировать. Среди этих событий — нажатия на кнопки (кнопки закрытия, URL-адреса, выбор продуктов и т. д.), а также уведомления о действиях, связанных с покупками, выполненных на пейволе. Ниже описано, как обрабатывать эти события.
Этот гайд предназначен только для пейволов нового Paywall Builder, которые требуют Adapty SDK версии 3.0 или выше.
Хотите посмотреть, как SDK Adapty интегрируется в реальное мобильное приложение? Изучите наши примеры приложений — в них показана полная настройка: отображение пейволов, совершение покупок и другие базовые функции.
Обработка событий в SwiftUI
Для управления и отслеживания процессов на экране пейвола в вашем мобильном приложении используйте модификатор .paywall в SwiftUI:
@State var paywallPresented = false
var body: some View {
Text("Hello, AdaptyUI!")
.paywall(
isPresented: $paywallPresented,
paywall: paywall,
viewConfiguration: viewConfig,
didPerformAction: { action in
switch action {
case .close:
paywallPresented = false
case let .openURL(url):
// handle opening the URL (incl. for terms and privacy)
default:
// handle other actions
}
},
didSelectProduct: { /* Handle the event */ },
didStartPurchase: { /* Handle the event */ },
didFinishPurchase: { product, info in /* Handle the event */ },
didFailPurchase: { product, error in /* Handle the event */ },
didStartRestore: { /* Handle the event */ },
didFinishRestore: { /* Handle the event */ },
didFailRestore: { /* Handle the event */ },
didFailRendering: { error in
paywallPresented = false
},
didFailLoadingProducts: { error in
return false
}
)
}Вы можете регистрировать только те параметры замыкания, которые вам нужны, и опускать те, которые не нужны. В этом случае неиспользуемые параметры замыкания не будут созданы.
| Параметр | Обязательный | Описание |
|---|---|---|
| isPresented | обязательный | Биндинг, управляющий отображением экрана пейвола. |
| paywallConfiguration | обязательный | Объект AdaptyUI.PaywallConfiguration, содержащий визуальные настройки пейвола. Используйте метод AdaptyUI.paywallConfiguration(for:products:viewConfiguration:observerModeResolver:tagResolver:timerResolver:). Подробнее см. в разделе Получение пейволов Paywall Builder и их конфигурации. |
| didFailPurchase | обязательный | Вызывается при ошибке покупки (например, платёж не разрешён, проблемы с сетью, неверный продукт). Не вызывается при отмене пользователем или ожидающих платежах. |
| didFinishRestore | обязательный | Вызывается при успешном завершении покупки. |
| didFailRestore | обязательный | Вызывается при ошибке восстановления покупки. |
| didFailRendering | обязательный | Вызывается при ошибке рендеринга интерфейса. В этом случае обратитесь в поддержку Adapty. |
| fullScreen | необязательный | Определяет, отображается ли пейвол в полноэкранном режиме или как модальное окно. По умолчанию true. |
| didAppear | необязательный | Вызывается, когда экран пейвола появляется на экране. Также вызывается, когда пользователь нажимает кнопку веб-пейвола внутри пейвола и веб-пейвол открывается во встроенном браузере. |
| didDisappear | необязательный | Вызывается, когда экран пейвола закрывается. Также вызывается, когда веб-пейвол, открытый из пейвола во встроенном браузере, исчезает с экрана. |
| didPerformAction | необязательный | Вызывается при нажатии пользователем кнопки. У разных кнопок разные идентификаторы действий. Два идентификатора заданы заранее: close и openURL, остальные — пользовательские и задаются в билдере. |
| didSelectProduct | необязательный | Вызывается, если продукт выбран для покупки — пользователем или системой. |
| didStartPurchase | необязательный | Вызывается, когда пользователь начинает процесс покупки. |
| didFinishPurchase | необязательный | Вызывается при успешном завершении покупки. |
| didFinishWebPaymentNavigation | необязательный | Вызывается после попытки открыть веб-пейвол для покупки — успешной или нет. |
| didStartRestore | необязательный | Вызывается, когда пользователь начинает процесс восстановления покупок. |
| didFailLoadingProducts | необязательный | Вызывается при ошибках загрузки продуктов. Верните true, чтобы повторить загрузку. |
| didPartiallyLoadProducts | необязательный | Вызывается, когда продукты загружены частично. |
| showAlertItem | необязательный | Биндинг, управляющий отображением элементов алерта поверх пейвола. |
| showAlertBuilder | необязательный | Функция для рендеринга представления алерта. |
| placeholderBuilder | необязательный | Функция для рендеринга заглушки во время загрузки пейвола. |
Обработка событий в UIKit
Чтобы управлять процессами на экране пейвола или отслеживать их, реализуйте методы AdaptyPaywallControllerDelegate.
События, генерируемые пользователем
Выбор продукта
Если пользователь выбирает продукт для покупки, будет вызван следующий метод:
func paywallController(
_ controller: AdaptyPaywallController,
didSelectProduct product: AdaptyPaywallProductWithoutDeterminingOffer
) { }Пример события (нажмите, чтобы раскрыть)
{
"product": {
"vendorProductId": "premium_monthly",
"localizedTitle": "Premium Monthly",
"localizedDescription": "Premium subscription for 1 month",
"localizedPrice": "$9.99",
"price": 9.99,
"currencyCode": "USD"
}
}Покупка начата
Если пользователь инициирует процесс покупки, будет вызван этот метод:
func paywallController(_ controller: AdaptyPaywallController,
didStartPurchase product: AdaptyPaywallProduct) {
}Пример события (нажмите, чтобы развернуть)
{
"product": {
"vendorProductId": "premium_monthly",
"localizedTitle": "Premium Monthly",
"localizedDescription": "Premium subscription for 1 month",
"localizedPrice": "$9.99",
"price": 9.99,
"currencyCode": "USD"
}
}Не вызывается в режиме Observer. Подробнее см. в разделе iOS — отображение пейволов Paywall Builder в режиме Observer.
Покупка через веб-пейвол
Если пользователь инициирует процесс покупки через веб-пейвол, будет вызван этот метод:
func paywallController(
_ controller: AdaptyPaywallController,
shouldContinueWebPaymentNavigation product: AdaptyPaywallProduct
) {
}Пример события (нажмите, чтобы развернуть)
{
"product": {
"vendorProductId": "premium_monthly",
"localizedTitle": "Premium Monthly",
"localizedDescription": "Premium subscription for 1 month",
"localizedPrice": "$9.99",
"price": 9.99,
"currencyCode": "USD"
}
}Успешная или отменённая покупка
Если покупка прошла успешно, будет вызван следующий метод:
func paywallController(
_ controller: AdaptyPaywallController,
didFinishPurchase product: AdaptyPaywallProductWithoutDeterminingOffer,
purchaseResult: AdaptyPurchaseResult
) { }
}Примеры событий (нажмите, чтобы развернуть)
// Successful purchase
{
"product": {
"vendorProductId": "premium_monthly",
"localizedTitle": "Premium Monthly",
"localizedDescription": "Premium subscription for 1 month",
"localizedPrice": "$9.99",
"price": 9.99,
"currencyCode": "USD"
},
"purchaseResult": {
"type": "success",
"profile": {
"accessLevels": {
"premium": {
"id": "premium",
"isActive": true,
"expiresAt": "2024-02-15T10:30:00Z"
}
}
}
}
}
// Cancelled purchase
{
"product": {
"vendorProductId": "premium_monthly",
"localizedTitle": "Premium Monthly",
"localizedDescription": "Premium subscription for 1 month",
"localizedPrice": "$9.99",
"price": 9.99,
"currencyCode": "USD"
},
"purchaseResult": {
"type": "cancelled"
}
}Мы рекомендуем в этом случае закрывать экран пейвола.
В режиме Observer mode это не вызывается. Подробнее см. в разделе iOS — отображение пейволов Paywall Builder в режиме Observer mode.
Неудачная покупка
Если покупка завершается с ошибкой, вызывается этот метод. Сюда входят ошибки StoreKit (ограничения платежей, неверные продукты, сетевые сбои), сбои верификации транзакций и системные ошибки. Обратите внимание: отмена пользователем вызывает didFinishPurchase с результатом «отменено», а ожидающие платежи этот метод не вызывают.
func paywallController(
_ controller: AdaptyPaywallController,
didFailPurchase product: AdaptyPaywallProduct,
error: AdaptyError
) { }Пример события (нажмите, чтобы развернуть)
{
"product": {
"vendorProductId": "premium_monthly",
"localizedTitle": "Premium Monthly",
"localizedDescription": "Premium subscription for 1 month",
"localizedPrice": "$9.99",
"price": 9.99,
"currencyCode": "USD"
},
"error": {
"code": "purchase_failed",
"message": "Purchase failed due to insufficient funds",
"details": {
"underlyingError": "Insufficient funds in account"
}
}
}Не вызывается в режиме Observer mode. Подробнее см. в разделе iOS — Отображение пейволов Paywall Builder в режиме Observer mode.
Ошибка покупки через веб-пейвол
Если Adapty.openWebPaywall() завершается с ошибкой, будет вызван этот метод:
func paywallController(
_ controller: AdaptyPaywallController,
didFailWebPaymentNavigation product: AdaptyPaywallProduct,
error: AdaptyError
) { }Пример события (нажмите, чтобы развернуть)
{
"product": {
"vendorProductId": "premium_monthly",
"localizedTitle": "Premium Monthly",
"localizedDescription": "Premium subscription for 1 month",
"localizedPrice": "$9.99",
"price": 9.99,
"currencyCode": "USD"
},
"error": {
"code": "web_payment_failed",
"message": "Web payment navigation failed",
"details": {
"underlyingError": "Network connection error"
}
}
}Успешное восстановление покупки
Если восстановление покупки прошло успешно, будет вызван этот метод:
func paywallController(
_ controller: AdaptyPaywallController,
didFinishRestoreWith profile: AdaptyProfile
) { }Пример события (нажмите, чтобы развернуть)
{
"profile": {
"accessLevels": {
"premium": {
"id": "premium",
"isActive": true,
"expiresAt": "2024-02-15T10:30:00Z"
}
},
"subscriptions": [
{
"vendorProductId": "premium_monthly",
"isActive": true,
"expiresAt": "2024-02-15T10:30:00Z"
}
]
}
}Мы рекомендуем закрывать экран, если у пользователя есть нужный accessLevel. Обратитесь к разделу Статус подписки, чтобы узнать, как его проверить.
Ошибка восстановления покупок
Если восстановление покупки завершится ошибкой, будет вызван этот метод:
public func paywallController(
_ controller: AdaptyPaywallController,
didFailRestoreWith error: AdaptyError
) { }Пример события (нажмите, чтобы развернуть)
{
"error": {
"code": "restore_failed",
"message": "Purchase restoration failed",
"details": {
"underlyingError": "No previous purchases found"
}
}
}Загрузка данных и рендеринг
Ошибки загрузки продуктов
Если вы не передаёте массив продуктов при инициализации, AdaptyUI самостоятельно получит нужные объекты с сервера. Если эта операция завершится ошибкой, AdaptyUI сообщит об этом через следующий метод:
public func paywallController(
_ controller: AdaptyPaywallController,
didFailLoadingProductsWith error: AdaptyError
) -> Bool {
return true
}Пример события (нажмите, чтобы развернуть)
{
"error": {
"code": "products_loading_failed",
"message": "Failed to load products from the server",
"details": {
"underlyingError": "Network timeout"
}
}
}Если вы вернёте true, AdaptyUI повторит запрос через 2 секунды.
Ошибки рендеринга
Если во время рендеринга интерфейса возникает ошибка, она будет передана через этот метод:
public func paywallController(
_ controller: AdaptyPaywallController,
didFailRenderingWith error: AdaptyError
) { }Пример события (нажмите, чтобы развернуть)
{
"error": {
"code": "rendering_failed",
"message": "Failed to render paywall interface",
"details": {
"underlyingError": "Invalid paywall configuration"
}
}
}В обычной ситуации такие ошибки не должны возникать, поэтому если вы с ними столкнулись — сообщите нам.