Gestionar eventos del paywall en el SDK de 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 paywalls configurados con el Paywall Builder no necesitan código adicional para realizar y restaurar compras. Sin embargo, generan algunos eventos a los que tu app puede responder. Estos eventos incluyen pulsaciones de botones (botones de cierre, URLs, selección de productos, etc.) así como notificaciones sobre acciones relacionadas con compras realizadas en el paywall. A continuación aprenderás cómo responder a estos eventos.

Esta guía es únicamente 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.

Gestionar eventos en SwiftUI

Para controlar o supervisar los procesos que ocurren en la pantalla del paywall dentro de tu app, 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 closure que necesites y omitir los que no uses. En ese caso, los parámetros de closure 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 por cancelaciones del usuario ni pagos pendientes.
didFinishRestorerequeridoSe invoca cuando la compra se completa correctamente.
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 pantalla completa o como modal. Por defecto 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. Distintos botones tienen distintos IDs de acción. Dos IDs están predefinidos: close y openURL, mientras que el resto son personalizados y se pueden configurar en el builder.
didSelectProductopcionalSi se selecciona un producto para comprar (por el usuario o por el sistema), se invocará este callback.
didStartPurchaseopcionalSe invoca cuando el usuario inicia el proceso de compra.
didFinishPurchaseopcionalSe invoca cuando la compra se completa correctamente.
didFinishWebPaymentNavigationopcionalSe invoca tras intentar abrir un web paywall para realizar una compra, tanto si tiene éxito como si falla.
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.

Gestionar eventos en UIKit

Para controlar o supervisar 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 el usuario selecciona un producto para comprar, 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 el usuario inicia el proceso de compra, se invocará este método:

func paywallController(_ controller: AdaptyPaywallController,
                       didStartPurchase product: AdaptyPaywallProduct) {
}
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"
  }
}

No se invocará en el modo Observer. Consulta el tema iOS - Presentar paywalls del Paywall Builder en modo Observer para más detalles.

Compra iniciada mediante un web paywall

Si el usuario inicia el proceso de compra usando un web paywall, se invocará este método:

func paywallController(
        _ controller: AdaptyPaywallController,
        shouldContinueWebPaymentNavigation product: AdaptyPaywallProduct
    ) {
    }
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 completada o cancelada

Si la compra tiene éxito, se invocará este método:

func paywallController(
    _ controller: AdaptyPaywallController,
    didFinishPurchase product: AdaptyPaywallProductWithoutDeterminingOffer,
    purchaseResult: AdaptyPurchaseResult
) { }
}
Ejemplos de evento (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"
  }
}

En ese caso, te recomendamos cerrar la pantalla del paywall.

No se invocará en el modo Observer. Consulta el tema iOS - Presentar paywalls del 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 inválidos, fallos de red), fallos en la verificación de transacciones y errores del sistema. Ten en cuenta que las cancelaciones del usuario activan didFinishPurchase con un resultado 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 - Presentar paywalls del Paywall Builder en modo Observer para más detalles.

Compra fallida mediante un web paywall

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 completada

Si la restauración de una compra tiene éxito, 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"
      }
    ]
  }
}

Te recomendamos cerrar la pantalla si el usuario tiene el accessLevel requerido. Consulta el tema Estado de la suscripción para aprender a comprobarlo.

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 notificará 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 ocurre 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 expandir)
{
  "error": {
    "code": "rendering_failed",
    "message": "Failed to render paywall interface",
    "details": {
      "underlyingError": "Invalid paywall configuration"
    }
  }
}

En condiciones normales, estos errores no deberían producirse, así que si te encuentras con alguno, comunícanoslo.