Manejar eventos de flow y paywall - iOS
Esta guía cubre el manejo de eventos para compras, restauraciones, selección de productos y renderizado de paywalls. También debes implementar el manejo de botones (cerrar el paywall, abrir enlaces, etc.). Consulta nuestra guía sobre el manejo de acciones de botones para más detalles.
Los flows y paywalls no necesitan código adicional para realizar y restaurar compras. Sin embargo, generan ciertos eventos a los que tu app puede reaccionar. Estos eventos incluyen pulsaciones de botones (botones de cierre, URLs, selección de productos, etc.) así como notificaciones sobre acciones relacionadas con compras. Aprende a responder a estos eventos a continuación.
¿Quieres ver un ejemplo real de cómo integrar el SDK de Adapty en una app móvil? Consulta nuestras apps de muestra, que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas.
Manejo de eventos en SwiftUI
Para controlar o supervisar los procesos que ocurren en la pantalla del flow o paywall dentro de tu app, usa el modificador .flow en 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 */ },
didFinishPurchase: { product, purchaseResult in /* dismiss, or do nothing to let the flow continue */ },
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
}
)
}Puedes registrar solo los parámetros del closure que necesites y omitir los que no.
| Parámetro | Requerido | Descripción |
|---|---|---|
| isPresented | requerido | Un binding que gestiona si la pantalla del flow o paywall está visible. |
| flowConfiguration | requerido | Un objeto AdaptyUI.FlowConfiguration que contiene los detalles visuales del flow o paywall. Consulta Obtener flows y paywalls para más detalles. |
| didFinishPurchase | requerido | Se invoca cuando Adapty.makePurchase() finaliza correctamente. El flow no se cierra de forma automática — establece tu binding de presentación a false aquí, o no hagas nada para que el flow continúe tras la compra. |
| didFailPurchase | requerido | Se invoca cuando Adapty.makePurchase() falla. |
| didFinishRestore | requerido | Se invoca cuando Adapty.restorePurchases() finaliza correctamente. |
| didFailRestore | requerido | Se invoca cuando Adapty.restorePurchases() falla. |
| didReceiveError | requerido | Se invoca cuando el flow encuentra un error de renderizado o un error en tiempo de ejecución del script del flow (por ejemplo, una excepción de JavaScript, código AdaptyUIError 4105). En caso de error de renderizado, contacta con el soporte de Adapty. |
| placeholderBuilder | opcional | Una función para renderizar la vista de marcador de posición mientras se carga el flow o paywall. Por defecto muestra un ProgressView. |
| fullScreen | opcional | Determina si el flow o paywall aparece en modo pantalla completa o como una hoja. Por defecto es true. |
| didAppear | opcional | Se invoca cuando la vista del flow o paywall aparece en pantalla. |
| didDisappear | opcional | Se invoca cuando la vista del flow o paywall fue cerrada. |
| didPerformAction | opcional | Se invoca cuando el usuario pulsa un botón. Hay dos IDs de acción predefinidos: close y openURL; el resto son personalizados y se pueden configurar en el builder. |
| didSelectProduct | opcional | Se invoca cuando el usuario o el sistema selecciona un producto para comprar. |
| didStartPurchase | opcional | Se invoca cuando el usuario inicia el proceso de compra. |
| didFinishWebPaymentNavigation | opcional | Se invoca cuando finaliza la navegación del pago web. |
| didStartRestore | opcional | Se invoca cuando el usuario inicia el proceso de restauración. |
| didFailLoadingProducts | opcional | Se invoca cuando se producen errores al cargar los productos. Devuelve true para reintentar la carga. |
| didPartiallyLoadProducts | opcional | Se invoca cuando los productos se cargan parcialmente. |
| showAlertItem | opcional | Un binding que gestiona la visualización de elementos de alerta sobre el flow o paywall. |
| showAlertBuilder | opcional | Una función para renderizar la vista de alerta. |
Gestión de eventos en UIKit
Para apps con UIKit, los eventos se gestionan mediante el protocolo AdaptyFlowControllerDelegate. Consulta Mostrar flows y paywalls - iOS para saber cómo configurar AdaptyFlowController con AdaptyFlowControllerDelegate.
El protocolo declara 13 métodos. Cuatro de ellos no tienen implementación por defecto y deben implementarse al conformar: didFinishPurchase, didFailPurchase, didFinishRestoreWith y didFailRestoreWith. El resto proporcionan implementaciones no-op por defecto y pueden sobreescribirse cuando se desee un comportamiento personalizado. Los métodos se agrupan a continuación por propósito.
Ciclo de vida
func flowControllerDidAppear(_ controller: AdaptyFlowController) { }
func flowControllerDidDisappear(_ controller: AdaptyFlowController) { }Estos se activan cuando la vista del flow o del paywall se presenta y se cierra.
Acciones del usuario
func flowController(
_ controller: AdaptyFlowController,
didPerform action: AdaptyUI.Action
) { }Casos de AdaptyUI.Action:
.close— el comportamiento predeterminado descarta el controlador. Sobreescríbelo si quieres mantener el controlador en pantalla o ejecutar limpieza adicional..openURL(url:)— el comportamiento predeterminado abre la URL conUIApplication.shared.open(...)..custom(id:)— se dispara para botones con un ID de acción personalizado definido en el builder.
Selección de producto
func flowController(
_ controller: AdaptyFlowController,
didSelectProduct product: AdaptyPaywallProduct
) { }Se invoca cuando el usuario o el sistema selecciona un producto para su compra. El producto incluye toda la información de la oferta (la elegibilidad se determina automáticamente en v4 — no existe un tipo AdaptyPaywallProductWithoutDeterminingOffer separado).
Eventos de compra
func flowController(
_ controller: AdaptyFlowController,
didStartPurchase product: AdaptyPaywallProduct
) { }
func flowController(
_ controller: AdaptyFlowController,
didFinishPurchase product: AdaptyPaywallProduct,
purchaseResult: AdaptyPurchaseResult
) {
if !purchaseResult.isPurchaseCancelled {
controller.dismiss(animated: true) // or do nothing to let the flow continue
}
}
func flowController(
_ controller: AdaptyFlowController,
didFailPurchase product: AdaptyPaywallProduct,
error: AdaptyError
) { }didFinishPurchase y didFailPurchase no tienen implementaciones por defecto y deben implementarse. El controlador no se cierra automáticamente tras una compra exitosa — llama a controller.dismiss(animated:) cuando sea apropiado, o no hagas nada para que un flujo de múltiples pantallas continúe tras la compra.
Eventos de restauración
func flowControllerDidStartRestore(_ controller: AdaptyFlowController) { }
func flowController(
_ controller: AdaptyFlowController,
didFinishRestoreWith profile: AdaptyProfile
) { }
func flowController(
_ controller: AdaptyFlowController,
didFailRestoreWith error: AdaptyError
) { }didFinishRestoreWith y didFailRestoreWith no tienen implementaciones por defecto. Comprueba si el AdaptyProfile devuelto contiene el nivel de acceso que necesitas antes de cerrar el controlador.
Errores del flow y errores de carga de productos
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 se activa para errores de renderizado y para errores de ejecución del script del flow (excepciones de JavaScript, código AdaptyUIError 4105). Para errores de renderizado, contacta con el soporte de Adapty. Para errores de carga, devuelve true desde didFailLoadingProductsWith para reintentar — útil para fallos de red transitorios.
Navegación de pago web
func flowController(
_ controller: AdaptyFlowController,
didFinishWebPaymentNavigation product: AdaptyPaywallProduct?,
error: AdaptyError?
) { }Se invoca cuando finaliza una navegación de pago web, ya sea con éxito o con error.
Esta guía cubre el manejo de eventos para compras, restauraciones, selección de productos y renderizado de paywalls. También debes implementar el manejo de botones (cerrar paywall, abrir enlaces, etc.). Consulta nuestra guía sobre el manejo de acciones de botones para más detalles.
Los paywalls configurados con Paywall Builder no necesitan código adicional para realizar ni restaurar compras. Sin embargo, generan algunos eventos a los que tu app puede responder. Estos eventos incluyen pulsaciones de botones (botones de cierre, URLs, selecciones de productos, etc.), así como notificaciones sobre acciones relacionadas con compras realizadas en el paywall. A continuación se explica cómo responder a estos eventos.
Esta guía es exclusivamente para paywalls del nuevo Paywall Builder que requieren Adapty SDK v3.0 o posterior.
¿Quieres ver un ejemplo real de cómo se integra el SDK de Adapty en una app móvil? Echa un vistazo a nuestras apps de ejemplo, que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas.
Manejo de eventos en SwiftUI
Para controlar o monitorear los procesos que ocurren en la pantalla del paywall dentro de tu aplicación móvil, usa el modificador .paywall en 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
}
)
}Puedes registrar solo los parámetros de cierre que necesites y omitir los que no. En ese caso, los parámetros de cierre no utilizados no se crearán.
| Parámetro | Requerido | Descripción |
|---|---|---|
| isPresented | requerido | Un binding que gestiona si la pantalla del paywall se muestra o no. |
| paywallConfiguration | requerido | Un objeto AdaptyUI.PaywallConfiguration que contiene los detalles visuales del paywall. Usa el método AdaptyUI.paywallConfiguration(for:products:viewConfiguration:observerModeResolver:tagResolver:timerResolver:). Consulta el tema Obtener paywalls del Paywall Builder y su configuración para más detalles. |
| didFailPurchase | requerido | Se invoca cuando una compra falla por errores (p. ej., pago no permitido, problemas de red, producto inválido). No se invoca ante cancelaciones del usuario ni pagos pendientes. |
| didFinishRestore | requerido | Se invoca cuando la compra se completa con éxito. |
| didFailRestore | requerido | Se invoca cuando falla la restauración de una compra. |
| didFailRendering | requerido | Se invoca si ocurre un error al renderizar la interfaz. En ese caso, contacta con el soporte de Adapty. |
| fullScreen | opcional | Determina si el paywall aparece en modo pantalla completa o como modal. El valor predeterminado es true. |
| didAppear | opcional | Se invoca cuando la vista del paywall aparece en pantalla. También se invoca cuando el usuario pulsa el botón de web paywall dentro de un paywall y se abre un web paywall en un navegador in-app. |
| didDisappear | opcional | Se invoca cuando la vista del paywall se cierra. También se invoca cuando un web paywall abierto desde un paywall en un navegador in-app desaparece de la pantalla. |
| didPerformAction | opcional | Se invoca cuando el usuario pulsa un botón. Cada botón tiene un ID de acción diferente. Hay dos IDs de acción predefinidos: close y openURL; el resto son personalizados y se pueden configurar en el builder. |
| didSelectProduct | opcional | Se invoca cuando se selecciona un producto para la compra (ya sea por el usuario o por el sistema). |
| didStartPurchase | opcional | Se invoca cuando el usuario inicia el proceso de compra. |
| didFinishPurchase | opcional | Se invoca cuando la compra se completa con éxito. |
| didFinishWebPaymentNavigation | opcional | Se invoca tras intentar abrir un web paywall para la compra, tanto si se ha completado correctamente como si ha fallado. |
| didStartRestore | opcional | Se invoca cuando el usuario inicia el proceso de restauración. |
| didFailLoadingProducts | opcional | Se invoca cuando ocurren errores durante la carga de productos. Devuelve true para reintentar la carga. |
| didPartiallyLoadProducts | opcional | Se invoca cuando los productos se cargan parcialmente. |
| showAlertItem | opcional | Un binding que gestiona la visualización de elementos de alerta sobre el paywall. |
| showAlertBuilder | opcional | Una función para renderizar la vista de alerta. |
| placeholderBuilder | opcional | Una función para renderizar la vista de marcador de posición mientras se carga el paywall. |
Manejo de eventos en UIKit
Para controlar o monitorizar los procesos que ocurren en la pantalla del paywall dentro de tu app, implementa los métodos de AdaptyPaywallControllerDelegate.
Eventos generados por el usuario
Selección de producto
Si un usuario selecciona un producto para comprarlo, se invocará este método:
func paywallController(
_ controller: AdaptyPaywallController,
didSelectProduct product: AdaptyPaywallProductWithoutDeterminingOffer
) { }Ejemplo de evento (haz clic para expandir)
{
"product": {
"vendorProductId": "premium_monthly",
"localizedTitle": "Premium Monthly",
"localizedDescription": "Premium subscription for 1 month",
"localizedPrice": "$9.99",
"price": 9.99,
"currencyCode": "USD"
}
}Compra iniciada
Si un usuario inicia el proceso de compra, se invocará este método:
func paywallController(_ controller: AdaptyPaywallController,
didStartPurchase product: AdaptyPaywallProduct) {
}Ejemplo de evento (clic para expandir)
{
"product": {
"vendorProductId": "premium_monthly",
"localizedTitle": "Premium Monthly",
"localizedDescription": "Premium subscription for 1 month",
"localizedPrice": "$9.99",
"price": 9.99,
"currencyCode": "USD"
}
}No se invocará en el modo Observer. Consulta el tema iOS - Presentar paywalls de Paywall Builder en modo Observer para más detalles.
Compra iniciada desde un paywall web
Si un usuario inicia el proceso de compra mediante un paywall web, se invocará este método:
func paywallController(
_ controller: AdaptyPaywallController,
shouldContinueWebPaymentNavigation product: AdaptyPaywallProduct
) {
}Ejemplo de evento (haz clic para ampliar)
{
"product": {
"vendorProductId": "premium_monthly",
"localizedTitle": "Premium Monthly",
"localizedDescription": "Premium subscription for 1 month",
"localizedPrice": "$9.99",
"price": 9.99,
"currencyCode": "USD"
}
}Compra exitosa o cancelada
Si la compra se realiza con éxito, se invocará este método:
func paywallController(
_ controller: AdaptyPaywallController,
didFinishPurchase product: AdaptyPaywallProductWithoutDeterminingOffer,
purchaseResult: AdaptyPurchaseResult
) { }
}Ejemplos de eventos (haz clic para expandir)
// 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"
}
}Recomendamos cerrar la pantalla del paywall en ese caso.
No se invocará en el modo Observer. Consulta el tema iOS - Presentar paywalls de Paywall Builder en modo Observer para más detalles.
Compra fallida
Si una compra falla por un error, se invocará este método. Esto incluye errores de StoreKit (restricciones de pago, productos no válidos, fallos de red), errores de verificación de transacciones y errores del sistema. Ten en cuenta que las cancelaciones del usuario activan didFinishPurchase con un resultado de cancelado, y los pagos pendientes no activan este método.
func paywallController(
_ controller: AdaptyPaywallController,
didFailPurchase product: AdaptyPaywallProduct,
error: AdaptyError
) { }Ejemplo de evento (haz clic para expandir)
{
"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"
}
}
}No se invocará en el modo Observer. Consulta el tema iOS - Mostrar paywalls del Paywall Builder en modo Observer para más detalles.
Compra fallida mediante un paywall web
Si Adapty.openWebPaywall() falla, se invocará este método:
func paywallController(
_ controller: AdaptyPaywallController,
didFailWebPaymentNavigation product: AdaptyPaywallProduct,
error: AdaptyError
) { }Ejemplo de evento (Haz clic para expandir)
{
"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"
}
}
}Restauración exitosa
Si la restauración de una compra se realiza correctamente, se invocará este método:
func paywallController(
_ controller: AdaptyPaywallController,
didFinishRestoreWith profile: AdaptyProfile
) { }Ejemplo de evento (haz clic para expandir)
{
"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"
}
]
}
}Recomendamos cerrar la pantalla si el usuario tiene el accessLevel requerido. Consulta el tema Estado de la suscripción para saber cómo verificarlo.
Restauración fallida
Si la restauración de una compra falla, se invocará este método:
public func paywallController(
_ controller: AdaptyPaywallController,
didFailRestoreWith error: AdaptyError
) { }Ejemplo de evento (haz clic para expandir)
{
"error": {
"code": "restore_failed",
"message": "Purchase restoration failed",
"details": {
"underlyingError": "No previous purchases found"
}
}
}Obtención de datos y renderizado
Errores al cargar productos
Si no pasas el array de productos durante la inicialización, AdaptyUI recuperará los objetos necesarios del servidor por sí solo. Si esta operación falla, AdaptyUI reportará el error llamando a este método:
public func paywallController(
_ controller: AdaptyPaywallController,
didFailLoadingProductsWith error: AdaptyError
) -> Bool {
return true
}Ejemplo de evento (haz clic para expandir)
{
"error": {
"code": "products_loading_failed",
"message": "Failed to load products from the server",
"details": {
"underlyingError": "Network timeout"
}
}
}Si devuelves true, AdaptyUI repetirá la solicitud después de 2 segundos.
Errores de renderizado
Si se produce un error durante el renderizado de la interfaz, se notificará mediante este método:
public func paywallController(
_ controller: AdaptyPaywallController,
didFailRenderingWith error: AdaptyError
) { }Ejemplo de evento (haz clic para ampliar)
{
"error": {
"code": "rendering_failed",
"message": "Failed to render paywall interface",
"details": {
"underlyingError": "Invalid paywall configuration"
}
}
}En una situación normal, estos errores no deberían producirse, así que si te encuentras con alguno, por favor, comunícanoslo.