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 */ },
            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ámetroObligatorioDescripción
isPresentedobligatorioUn binding que controla si se muestra la pantalla del flow o del paywall.
flowConfigurationobligatorioUn objeto AdaptyUI.FlowConfiguration que contiene los detalles visuales del flow o del paywall. Consulta Obtener flows y paywalls para más información.
didFailPurchaseobligatorioSe invoca cuando Adapty.makePurchase() falla.
didFinishRestoreobligatorioSe invoca cuando Adapty.restorePurchases() se completa correctamente.
didFailRestoreobligatorioSe invoca cuando Adapty.restorePurchases() falla.
didReceiveErrorobligatorioSe invoca cuando el flow encuentra un error de renderizado o un error en tiempo de ejecución en el 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.
placeholderBuilderopcionalUna función para renderizar la vista de marcador de posición mientras carga el flow o el paywall. Por defecto muestra un ProgressView.
fullScreenopcionalDetermina si el flow o el paywall aparece en modo pantalla completa o como una hoja. Por defecto es true.
didAppearopcionalSe invoca cuando la vista del flow o del paywall aparece en pantalla.
didDisappearopcionalSe invoca cuando la vista del flow o del paywall se cierra.
didPerformActionopcionalSe 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 definir en el builder.
didSelectProductopcionalSe invoca cuando el usuario o el sistema selecciona un producto para su compra.
didStartPurchaseopcionalSe invoca cuando el usuario inicia el proceso de compra.
didFinishPurchaseopcionalSe invoca cuando Adapty.makePurchase() se completa correctamente.
didFinishWebPaymentNavigationopcionalSe invoca cuando finaliza la navegación del pago web.
didStartRestoreopcionalSe invoca cuando el usuario inicia el proceso de restauración.
didFailLoadingProductsopcionalSe invoca cuando se producen errores durante la carga de productos. Devuelve true para reintentar la carga.
didPartiallyLoadProductsopcionalSe invoca cuando los productos se cargan de forma parcial.
showAlertItemopcionalUn binding que gestiona la visualización de elementos de alerta sobre el flow o el paywall.
showAlertBuilderopcionalUna función para renderizar la vista de alerta.

Gestión de eventos en UIKit

Para aplicaciones UIKit, los eventos se gestionan mediante el protocolo AdaptyFlowControllerDelegate. Consulta Display flows & paywalls - iOS para ver cómo configurar AdaptyFlowController con AdaptyFlowControllerDelegate.

El protocolo declara 13 métodos. Tres de ellos no tienen implementación por defecto y deben implementarse al conformar: didFailPurchase, didFinishRestoreWith y didFailRestoreWith. El resto ofrecen implementaciones predeterminadas vacías y pueden sobreescribirse cuando quieras un comportamiento personalizado. A continuación, los métodos están agrupados 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 con UIApplication.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
) {
    // Default: dismiss the controller unless the purchase was cancelled.
}

func flowController(
    _ controller: AdaptyFlowController,
    didFailPurchase product: AdaptyPaywallProduct,
    error: AdaptyError
) { }

didFailPurchase es el único evento de compra sin implementación predeterminada. didFinishPurchase cierra el controlador al completarse con éxito por defecto; sobreescríbelo solo si necesitas lógica personalizada 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ámetroRequeridoDescripción
isPresentedrequeridoUn binding que gestiona si la pantalla del paywall se muestra o no.
paywallConfigurationrequeridoUn 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.
didFailPurchaserequeridoSe 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.
didFinishRestorerequeridoSe invoca cuando la compra se completa con éxito.
didFailRestorerequeridoSe invoca cuando falla la restauración de una compra.
didFailRenderingrequeridoSe invoca si ocurre un error al renderizar la interfaz. En ese caso, contacta con el soporte de Adapty.
fullScreenopcionalDetermina si el paywall aparece en modo pantalla completa o como modal. El valor predeterminado es true.
didAppearopcionalSe 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.
didDisappearopcionalSe 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.
didPerformActionopcionalSe 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.
didSelectProductopcionalSe invoca cuando se selecciona un producto para la compra (ya sea por el usuario o por el sistema).
didStartPurchaseopcionalSe invoca cuando el usuario inicia el proceso de compra.
didFinishPurchaseopcionalSe invoca cuando la compra se completa con éxito.
didFinishWebPaymentNavigationopcionalSe invoca tras intentar abrir un web paywall para la compra, tanto si se ha completado correctamente como si ha fallado.
didStartRestoreopcionalSe invoca cuando el usuario inicia el proceso de restauración.
didFailLoadingProductsopcionalSe invoca cuando ocurren errores durante la carga de productos. Devuelve true para reintentar la carga.
didPartiallyLoadProductsopcionalSe invoca cuando los productos se cargan parcialmente.
showAlertItemopcionalUn binding que gestiona la visualización de elementos de alerta sobre el paywall.
showAlertBuilderopcionalUna función para renderizar la vista de alerta.
placeholderBuilderopcionalUna función para renderizar la vista de marcador de posición mientras se carga el paywall.

Manejo de eventos en UIKit

Para controlar o monitorear los procesos que ocurren en la pantalla del paywall dentro de tu aplicación móvil, 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.