# IOS - Adapty Documentation (Full Content)
This file contains the complete content of all documentation pages for this platform.
Locale: es
Generated on: 2026-05-22T06:52:52.272Z
Total files: 41
---
# File: sdk-installation-ios
---
---
title: "Instalar y configurar el SDK de iOS"
description: "Guía paso a paso para instalar el SDK de Adapty en iOS para apps con suscripciones."
---
El SDK de Adapty incluye dos módulos clave para integrarse fácilmente en tu app:
- **Core Adapty**: El SDK esencial, necesario para que Adapty funcione correctamente en tu app.
- **AdaptyUI**: Módulo opcional que necesitas si usas el [Adapty Paywall Builder](adapty-paywall-builder), una herramienta visual sin código para crear paywalls multiplataforma de forma sencilla.
:::tip
¿Quieres ver un ejemplo real de cómo se integra el SDK de Adapty en una app? Consulta nuestras [apps de ejemplo](https://github.com/adaptyteam/AdaptySDK-iOS/tree/master/Examples), que muestran la configuración completa: cómo mostrar paywalls, realizar compras y otras funciones básicas.
:::
Para ver una implementación completa paso a paso, también puedes ver los vídeos:
## Requisitos \{#requirements\}
Aunque el SDK es técnicamente compatible con iOS 13.0+ para el módulo principal, en la práctica se requiere iOS 15.0+ porque:
- Todas las funciones de StoreKit 2 requieren iOS 15.0+
- El módulo AdaptyUI solo es compatible con iOS 15.0+
:::important
Se requiere Adapty SDK 3.15.7+ al compilar con Xcode 26.4 o posterior.
:::
---
no_index: true
---
import Callout from '../../../components/Callout.astro';
Instalar el SDK es el paso 5 de la configuración de Adapty. Para que las compras funcionen en tu app, también necesitas conectar tu app a los stores, y luego crear productos, un paywall y un placement en el Adapty Dashboard. La [guía de inicio rápido](quickstart) explica todos los pasos necesarios.
## Instalar el SDK de Adapty \{#install-adapty-sdk\}
[](https://github.com/adaptyteam/AdaptySDK-iOS/releases)
En Xcode, ve a **File** -> **Add Package Dependency...**. Ten en cuenta que los pasos para añadir dependencias pueden variar según la versión de Xcode, así que consulta la documentación de Xcode si es necesario.
1. Introduce la URL del repositorio:
```
https://github.com/adaptyteam/AdaptySDK-iOS.git
```
2. Selecciona la versión (se recomienda la última versión estable) y haz clic en **Add Package**.
3. En la ventana **Choose Package Products**, selecciona los módulos que necesitas:
- **Adapty** (módulo principal)
- **AdaptyUI** (opcional - solo si planeas usar el Paywall Builder)
:::note
Nota:
- Para activar el [modo Kids](kids-mode), selecciona **Adapty_KidsMode** en lugar de **Adapty**.
- No selecciones ningún otro paquete de la lista, no los necesitas.
:::
4. Haz clic en **Add Package** para completar la instalación.
5. **Verifica la instalación:** En el navegador de tu proyecto deberías ver "Adapty" (y "AdaptyUI" si lo seleccionaste) bajo **Package Dependencies**.
:::info
CocoaPods está ahora en modo de mantenimiento y su desarrollo se ha detenido oficialmente. Recomendamos cambiar a [Swift Package Manager](sdk-installation-ios#install-adapty-sdk).
:::
1. Añade Adapty a tu `Podfile`. Elige los módulos que necesitas:
1. **Adapty** es el módulo obligatorio.
2. **AdaptyUI** es un módulo opcional que necesitas si planeas usar el [Adapty Paywall Builder](adapty-paywall-builder).
```shell showLineNumbers title="Podfile"
pod 'Adapty'
pod 'AdaptyUI' # optional module needed only for Paywall Builder
```
2. Ejecuta:
```sh showLineNumbers title="Shell"
pod install
```
Esto creará un archivo `.xcworkspace` para tu app. Usa este archivo para todo el desarrollo posterior.
## Activar el módulo Adapty del SDK de Adapty \{#activate-adapty-module-of-adapty-sdk\}
Activa el SDK de Adapty en el código de tu app.
:::note
El SDK de Adapty solo necesita activarse una vez en tu app.
:::
Para obtener tu **Public SDK Key**:
1. Ve al Adapty Dashboard y navega a [**App settings → General**](https://app.adapty.io/settings/general).
2. En la sección **Api keys**, copia la **Public SDK Key** (NO la Secret Key).
3. Reemplaza `"YOUR_PUBLIC_SDK_KEY"` en el código.
:::important
- Asegúrate de usar la **Public SDK key** para inicializar Adapty; la **Secret key** solo debe usarse para la [API del servidor](getting-started-with-server-side-api).
- Las **SDK keys** son únicas para cada app, así que si tienes varias apps asegúrate de elegir la correcta.
:::
```swift showLineNumbers
@main
struct YourApp: App {
init() {
// Configure Adapty SDK
let configurationBuilder = AdaptyConfiguration
.builder(withAPIKey: "YOUR_PUBLIC_SDK_KEY") // Get from Adapty dashboard
Adapty.logLevel = .verbose // recommended for development and the first production release
let config = configurationBuilder.build()
// Activate Adapty SDK asynchronously
Task {
do {
try await Adapty.activate(with: config)
} catch {
// Handle error appropriately for your app
print("Adapty activation failed: ", error)
}
}
var body: some Scene {
WindowGroup {
// Your content view
}
}
}
}
```
```swift showLineNumbers
// In your AppDelegate class:
// If you only use an AppDelegate, place the following code in the
// application(_:didFinishLaunchingWithOptions:) method.
// If you use a SceneDelegate, place the following code in the
// scene(_:willConnectTo:options:) method.
Task {
do {
let configurationBuilder = AdaptyConfiguration
.builder(withAPIKey: "YOUR_PUBLIC_SDK_KEY") // Get from Adapty dashboard
.with(logLevel: .verbose) // recommended for development and the first production release
let config = configurationBuilder.build()
try await Adapty.activate(with: config)
} catch {
// Handle error appropriately for your app
print("Adapty activation failed: ", error)
}
}
```
:::important
Espera a que `activate` se resuelva antes de llamar a cualquier otro método del SDK de Adapty. Consulta [Orden de llamadas en el SDK de iOS](ios-sdk-call-order) para ver la secuencia completa.
:::
Ahora configura los paywalls en tu app:
- Si usas el [Adapty Paywall Builder](adapty-paywall-builder), primero [activa el módulo AdaptyUI](#activate-adaptyui-module-of-adapty-sdk) a continuación, y luego sigue la [guía de inicio rápido del Paywall Builder](ios-quickstart-paywalls).
- Si construyes tu propia interfaz de paywall, consulta la [guía de inicio rápido para paywalls personalizados](ios-quickstart-manual).
## Activar el módulo AdaptyUI del SDK de Adapty \{#activate-adaptyui-module-of-adapty-sdk\}
Si planeas usar el [Paywall Builder](adapty-paywall-builder) y has [instalado el módulo AdaptyUI](sdk-installation-ios#install-adapty-sdk), también necesitas activar AdaptyUI.
:::important
En tu código, debes activar el módulo principal de Adapty antes de activar AdaptyUI.
:::
```swift showLineNumbers title="Swift"
@main
struct YourApp: App {
init() {
// ...ConfigurationBuilder steps
// Activate Adapty SDK asynchronously
Task {
do {
try await Adapty.activate(with: config)
try await AdaptyUI.activate()
} catch {
// Handle error appropriately for your app
print("Adapty activation failed: ", error)
}
}
// main body...
}
}
```
```swift showLineNumbers title="UIKit"
// If you only use an AppDelegate, place the following code in the
// application(_:didFinishLaunchingWithOptions:) method.
// If you use a SceneDelegate, place the following code in the
// scene(_:willConnectTo:options:) method.
Task {
do {
let configurationBuilder = AdaptyConfiguration
.builder(withAPIKey: "YOUR_PUBLIC_SDK_KEY") // Get from Adapty dashboard
.with(logLevel: .verbose) // recommended for development
let config = configurationBuilder.build()
try await Adapty.activate(with: config)
try await AdaptyUI.activate()
} catch {
// Handle error appropriately for your app
print("Adapty activation failed: ", error)
}
}
```
:::tip
De forma opcional, al activar AdaptyUI puedes [personalizar la configuración de caché para los paywalls](#set-up-media-cache-configuration-for-adaptyui).
:::
## Configuración opcional \{#optional-setup\}
### Registro de eventos \{#logging\}
#### Configurar el sistema de registro \{#set-up-the-logging-system\}
Adapty registra errores e información importante para ayudarte a entender qué está ocurriendo. Los niveles disponibles son:
| Nivel | Descripción |
| ---------- | ------------------------------------------------------------ |
| `error` | Solo se registran errores |
| `warn` | Se registran errores y mensajes del SDK que no causan errores críticos pero merecen atención |
| `info` | Se registran errores, advertencias y varios mensajes informativos |
| `verbose` | Se registra cualquier información adicional que pueda ser útil durante la depuración, como llamadas a funciones, consultas a la API, etc. |
```swift showLineNumbers
let configurationBuilder = AdaptyConfiguration
.builder(withAPIKey: "YOUR_PUBLIC_SDK_KEY")
.with(logLevel: .verbose) // recommended for development
```
#### Redirigir los mensajes del sistema de registro \{#redirect-the-logging-system-messages\}
Si necesitas enviar los mensajes de registro de Adapty a tu sistema o guardarlos en un archivo, usa el método `setLogHandler` e implementa tu lógica de registro personalizada dentro de él. Este handler recibe registros con el contenido del mensaje y su nivel de severidad.
```swift showLineNumbers title="Swift"
Adapty.setLogHandler { record in
writeToLocalFile("Adapty \(record.level): \(record.message)")
}
```
### Políticas de datos \{#data-policies\}
Adapty no almacena datos personales de tus usuarios a menos que los envíes explícitamente, pero puedes implementar políticas adicionales de seguridad de datos para cumplir con las directrices de la store o de tu país.
#### Deshabilitar la recopilación y el uso compartido del IDFA \{#disable-idfa-collection-and-sharing\}
Al activar el módulo Adapty, establece `idfaCollectionDisabled` en `true` para deshabilitar la recopilación y el uso compartido del IDFA.
Usa este parámetro para cumplir con las directrices de revisión del App Store o para evitar activar el prompt de App Tracking Transparency cuando el IDFA no es necesario para tu app. El valor predeterminado es `false`. Para más detalles sobre la recopilación del IDFA, consulta la sección de [Integración de analíticas](analytics-integration#disable-collection-of-advertising-identifiers).
```swift showLineNumbers
let configurationBuilder =
AdaptyConfiguration
.builder(withAPIKey: "YOUR_PUBLIC_SDK_KEY")
.with(idfaCollectionDisabled: true)
```
#### Deshabilitar la recopilación y el uso compartido de la IP \{#disable-ip-collection-and-sharing\}
Al activar el módulo Adapty, establece `ipAddressCollectionDisabled` en `true` para deshabilitar la recopilación y el uso compartido de la dirección IP del usuario. El valor predeterminado es `false`.
Usa este parámetro para mejorar la privacidad del usuario, cumplir con normativas regionales de protección de datos (como el RGPD o el CCPA) o reducir la recopilación de datos innecesaria cuando las funciones basadas en IP no son necesarias para tu app.
```swift showLineNumbers
let configurationBuilder =
AdaptyConfiguration
.builder(withAPIKey: "YOUR_PUBLIC_SDK_KEY")
.with(ipAddressCollectionDisabled: true)
```
#### Configuración de caché de medios para paywalls en AdaptyUI \{#set-up-media-cache-configuration-for-adaptyui\}
Ten en cuenta que la configuración de AdaptyUI es opcional. Puedes activar el módulo AdaptyUI sin su configuración. Sin embargo, si usas la configuración, todos los parámetros son obligatorios.
```swift showLineNumbers title="Swift"
// Configure AdaptyUI
let adaptyUIConfiguration = AdaptyUI.Configuration(
mediaCacheConfiguration: .init(
memoryStorageTotalCostLimit: 100 * 1024 * 1024,
memoryStorageCountLimit: .max,
diskStorageSizeLimit: 100 * 1024 * 1024
)
)
// Activate AdaptyUI
AdaptyUI.activate(configuration: adaptyUIConfiguration)
```
Parámetros:
| Parámetro | Presencia | Descripción |
| :-------------------------- | :-------- | :----------------------------------------------------------- |
| memoryStorageTotalCostLimit | obligatorio | Límite de coste total del almacenamiento en bytes. |
| memoryStorageCountLimit | obligatorio | Límite de elementos del almacenamiento en memoria. |
| diskStorageSizeLimit | obligatorio | Límite de tamaño del archivo en disco en bytes. 0 significa sin límite. |
### Comportamiento de finalización de transacciones \{#transaction-finishing-behavior\}
:::info
Esta función está disponible a partir de la versión 3.12.0 del SDK.
:::
Por defecto, Adapty finaliza las transacciones automáticamente tras una validación exitosa. Sin embargo, si necesitas una validación avanzada de transacciones (como validación de recibos en el servidor, detección de fraude o lógica de negocio personalizada), puedes configurar el SDK para usar la finalización manual de transacciones.
```swift showLineNumbers title="Swift"
let configurationBuilder = AdaptyConfiguration
.builder(withAPIKey: "YOUR_PUBLIC_SDK_KEY")
.with(transactionsFinishBehavior: .manual) // .auto is the default
```
Consulta más detalles sobre cómo finalizar transacciones en la [guía](ios-transaction-management).
### Borrar datos al restaurar desde copia de seguridad \{#clear-data-on-backup-restore\}
Cuando `clearDataOnBackup` está establecido en `true`, el SDK detecta cuándo la app se restaura desde una copia de seguridad de iCloud y elimina todos los datos del SDK almacenados localmente, incluida la información de perfil en caché, los detalles de productos y los paywalls. El SDK se inicializa entonces con un estado limpio. El valor predeterminado es `false`.
:::note
Solo se elimina la caché local del SDK. El historial de transacciones con Apple y los datos del usuario en los servidores de Adapty no se modifican.
:::
```swift showLineNumbers
let configurationBuilder = AdaptyConfiguration
.builder(withAPIKey: "YOUR_PUBLIC_SDK_KEY")
.with(clearDataOnBackup: true) // default – false
```
## Resolución de problemas \{#troubleshooting\}
#### Error de concurrencia de Swift 6 con Tuist \{#swift-6-concurrency-error-with-tuist\}
Al compilar con [Tuist](https://tuist.dev/), puede que veas errores de compilación de concurrencia estricta de Swift 6. Los síntomas habituales incluyen errores de compatibilidad con el atributo `@Sendable` en `AdaptyUIBuilderLogic` u otros errores de Sendability entre módulos.
Esto ocurre porque Tuist genera proyectos Xcode a partir de paquetes SPM pero no conserva la configuración `swift-tools-version: 6.0`. Como resultado, algunos targets de Adapty (`Adapty`, `AdaptyUI`, `AdaptyUIBuilder`) se compilan con las reglas de Swift 5 mientras que otros usan Swift 6, lo que genera errores de `@Sendable` entre módulos.
**Solución**: Actualiza al SDK de Adapty **3.15.5** o posterior, que resuelve el problema independientemente de las versiones mixtas de Swift.
**Solución alternativa**: Si no puedes actualizar, establece explícitamente Swift 6 para los tres targets de Adapty en tu configuración de Tuist:
```swift showLineNumbers
targetSettings: [
"Adapty": .init().swiftVersion("6"),
"AdaptyUI": .init().swiftVersion("6"),
"AdaptyUIBuilder": .init().swiftVersion("6"),
]
```
#### Errores de compilación de Swift 6 causados por la sobreescritura de SWIFT_VERSION en el Podfile \{#swift-6-build-errors-caused-by-podfile-swift_version-override\}
Al compilar para iOS con [CocoaPods](sdk-installation-ios#install-adapty-sdk), puede que veas errores de compilación de Swift 6 en los targets de los pods de Adapty. Los síntomas habituales incluyen errores de `@Sendable` en `AdaptyUIBuilderLogic`, falta de conformidad con `Sendable` en tipos de Adapty, o errores de aislamiento de actores.
Los pods de Adapty declaran `s.swift_version = '6.0'` y requieren Swift 6 para compilar. Tu propio código de la app puede permanecer en Swift 5 — solo los targets de los pods de Adapty (`Adapty`, `AdaptyUI`, `AdaptyUIBuilder`, `AdaptyLogger`) necesitan compilarse con Swift 6.
La causa más común es un hook `post_install` en tu `Podfile` que sobreescribe `SWIFT_VERSION` para cada target de pod:
```ruby showLineNumbers title="Podfile"
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['SWIFT_VERSION'] = '5.9'
end
end
end
```
**Solución**: Excluye los targets de los pods de Adapty de la sobreescritura:
```ruby showLineNumbers title="Podfile"
post_install do |installer|
installer.pods_project.targets.each do |target|
next if %w[Adapty AdaptyUI AdaptyUIBuilder AdaptyLogger].include?(target.name)
target.build_configurations.each do |config|
config.build_settings['SWIFT_VERSION'] = '5.9'
end
end
end
```
Luego ejecuta `pod install` y vuelve a compilar.
Para verificarlo, abre `Pods.xcodeproj`, selecciona el target del pod `Adapty` → **Build Settings** → **Swift Language Version**. Debería ser **Swift 6**.
---
# File: ios-quickstart-paywalls
---
---
title: "Activar compras usando paywalls en el SDK de iOS"
description: "Guía de inicio rápido para configurar Adapty en la gestión de suscripciones in-app."
---
Para activar las compras in-app, necesitas entender tres conceptos clave:
- [**Productos**](product) – cualquier cosa que los usuarios pueden comprar (suscripciones, consumibles, acceso de por vida)
- [**Paywalls**](paywalls): configuraciones que definen qué productos ofrecer. En Adapty, los paywalls son la única manera de recuperar productos, pero este diseño te permite modificar ofertas, precios y combinaciones de productos sin tocar el código de tu app.
- [**Placements**](placements) – dónde y cuándo muestras los paywalls en tu app (como `main`, `onboarding`, `settings`). Configuras los paywalls para los placements en el dashboard y luego los solicitas por ID de placement en tu código. Esto facilita ejecutar pruebas A/B y mostrar distintos paywalls a diferentes usuarios.
Adapty te ofrece tres formas de activar las compras en tu app. Elige la que mejor se adapte a los requisitos de tu aplicación:
| Implementación | Complejidad | Cuándo usar |
|----------------------------|-------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Adapty Paywall Builder | ✅ Fácil | [Creas un paywall completo y listo para compras en el editor sin código](quickstart-paywalls). Adapty lo renderiza automáticamente y gestiona todo el flujo de compra, la validación de recibos y la gestión de suscripciones de forma transparente. |
| Paywalls creados manualmente | 🟡 Medio | Implementas la UI del paywall en el código de tu app, pero aun así obtienes el objeto paywall desde Adapty para mantener flexibilidad en las ofertas de productos. Consulta la [guía](ios-quickstart-manual). |
| Modo observador | 🔴 Difícil | Ya tienes tu propia infraestructura de gestión de compras y quieres seguir usándola. Ten en cuenta que el modo observador tiene sus limitaciones en Adapty. Consulta el [artículo](observer-vs-full-mode). |
:::important
**Los pasos a continuación muestran cómo implementar un paywall creado en el Paywall Builder de Adapty.**
Si no quieres usar el Paywall Builder, consulta la [guía para gestionar compras en paywalls creados manualmente](making-purchases).
:::
Para mostrar un paywall creado en el Paywall Builder de Adapty, solo necesitas hacer lo siguiente en el código de tu app:
1. **Obtener el paywall**: Obtén el paywall desde Adapty.
2. **Mostrar el paywall y Adapty gestionará las compras por ti**: Muestra el contenedor del paywall que obtuviste en tu app.
3. **Gestionar las acciones de los botones**: Asocia las interacciones del usuario con el paywall a la respuesta de tu app. Por ejemplo, abre enlaces o cierra el paywall cuando los usuarios pulsen botones.
## Antes de empezar \{#before-you-start\}
Antes de comenzar, completa estos pasos:
1. [Conecta tu app al App Store](initial_ios) en el Adapty Dashboard.
2. [Crea tus productos](create-product) en Adapty.
3. [Crea un paywall y añade productos](create-paywall).
4. [Crea un placement y añade tu paywall](create-placement).
5. [Instala y activa el SDK de Adapty](sdk-installation-ios) en el código de tu app.
:::tip
La forma más rápida de completar estos pasos es seguir la [guía de inicio rápido](quickstart) o crear paywalls y placements usando la [CLI para desarrolladores](developer-cli-quickstart).
:::
## 1. Obtener el paywall creado en el Paywall Builder \{#1-get-the-paywall-created-in-the-paywall-builder\}
Tus paywalls están asociados a placements configurados en el dashboard. Los placements te permiten mostrar distintos paywalls a diferentes audiencias o ejecutar [pruebas A/B](ab-tests).
Para obtener un paywall creado en el Paywall Builder de Adapty, debes:
1. Obtener el objeto `paywall` por el ID de [placement](placements) usando el método `getPaywall` y comprobar si es un paywall creado en el builder.
2. Obtener la configuración de vista del paywall usando el método `getPaywallConfiguration`. La configuración de vista contiene los elementos de UI y el estilo necesarios para mostrar el paywall.
:::important
Para obtener la configuración de vista, debes activar el toggle **Show on device** en el Paywall Builder. De lo contrario, obtendrás una configuración de vista vacía y el paywall no se mostrará.
:::
```swift
func loadPaywall() async {
let paywall = try await Adapty.getPaywall("YOUR_PLACEMENT_ID")
guard paywall.hasViewConfiguration else {
print("Paywall doesn't have view configuration")
return
}
paywallConfiguration = try await AdaptyUI.getPaywallConfiguration(forPaywall: paywall)
}
```
## 2. Mostrar el paywall \{#2-display-the-paywall\}
Ahora que tienes la configuración del paywall, basta con añadir unas pocas líneas para mostrarlo.
En SwiftUI, al mostrar el paywall también necesitas gestionar eventos. Algunos son opcionales, pero `didFailPurchase`, `didFinishRestore`, `didFailRestore` y `didFailRendering` son obligatorios. Para hacer pruebas, puedes copiar el código del fragmento de abajo para registrar estos errores.
:::tip
Gestionar `didFinishPurchase` no es obligatorio, pero es útil cuando quieres realizar acciones tras una compra exitosa. Si no implementas ese callback, el paywall se cerrará automáticamente.
:::
```swift
.paywall(
isPresented: $paywallPresented,
paywallConfiguration: paywallConfiguration,
didFailPurchase: { product, error in
print("Purchase failed: \(error)")
},
didFinishRestore: { profile in
print("Restore finished successfully")
},
didFailRestore: { error in
print("Restore failed: \(error)")
},
didFailRendering: { error in
paywallPresented = false
print("Rendering failed: \(error)")
},
showAlertItem: $alertItem
)
```
```swift
func presentPaywall(with config: AdaptyUI.PaywallConfiguration) {
let paywallController = AdaptyUI.paywallController(
with: config,
delegate: self
)
present(paywallController, animated: true)
}
```
:::info
Para más detalles sobre cómo mostrar un paywall, consulta nuestra [guía](ios-present-paywalls).
:::
## 3. Gestionar las acciones de los botones \{#3-handle-button-actions\}
Cuando los usuarios pulsan botones en el paywall, el SDK de iOS gestiona automáticamente las compras, la restauración, el cierre del paywall y la apertura de enlaces.
Sin embargo, otros botones tienen IDs personalizados o predefinidos y requieren gestionar las acciones en tu código. O puede que quieras sobreescribir su comportamiento predeterminado.
Por ejemplo, aquí se muestra el comportamiento predeterminado del botón de cierre. No necesitas añadirlo al código, pero aquí puedes ver cómo hacerlo si fuera necesario.
:::tip
Lee nuestras guías sobre cómo gestionar [acciones](handle-paywall-actions) y [eventos](ios-handling-events) de botones.
:::
```swift
didPerformAction: { action in
switch action {
case let .close:
paywallPresented = false // default behavior
default:
break
}
}
```
```swift
func paywallController(_ controller: AdaptyPaywallController,
didPerform action: AdaptyUI.Action) {
switch action {
case let .close:
controller.dismiss(animated: true) // default behavior
break
}
}
```
## Próximos pasos \{#next-steps\}
---
no_index: true
---
import Callout from '../../../components/Callout.astro';
¿Tienes preguntas o estás teniendo algún problema? Consulta nuestro [foro de soporte](https://adapty.featurebase.app/) donde encontrarás respuestas a preguntas frecuentes o podrás plantear las tuyas. ¡Nuestro equipo y la comunidad están aquí para ayudarte!
Tu paywall está listo para mostrarse en la app. [Prueba tus compras en modo sandbox](test-purchases-in-sandbox) para asegurarte de que puedes completar una compra de prueba desde el paywall.
Ahora necesitas [comprobar el nivel de acceso de los usuarios](ios-check-subscription-status) para asegurarte de mostrar un paywall o dar acceso a las funciones de pago a los usuarios correctos.
## Ejemplo completo \{#full-example\}
Aquí puedes ver cómo integrar todos los pasos de esta guía en tu app.
```swift
struct ContentView: View {
@State private var paywallPresented = false
@State private var alertItem: AlertItem?
@State private var paywallConfiguration: AdaptyUI.PaywallConfiguration?
@State private var isLoading = false
@State private var hasInitialized = false
var body: some View {
VStack {
if isLoading {
ProgressView("Loading...")
} else {
Text("Your App Content")
}
}
.task {
guard !hasInitialized else { return }
await initializePaywall()
hasInitialized = true
}
.paywall(
isPresented: $paywallPresented,
configuration: paywallConfiguration,
didPerformAction: { action in
switch action.type {
case let .close:
paywallPresented = false
default:
break
}
},
didFailPurchase: { product, error in
print("Purchase failed: \(error)")
},
didFinishRestore: { profile in
print("Restore finished successfully")
},
didFailRestore: { error in
print("Restore failed: \(error)")
},
didFailRendering: { error in
print("Rendering failed: \(error)")
},
showAlertItem: $alertItem
)
}
private func initializePaywall() async {
isLoading = true
defer { isLoading = false }
await loadPaywall()
paywallPresented = true
}
}
private func loadPaywall() async {
do {
let paywall = try await Adapty.getPaywall("YOUR_PLACEMENT_ID")
guard paywall.hasViewConfiguration else {
print("Paywall doesn't have view configuration")
return
}
paywallConfiguration = try await AdaptyUI.getPaywallConfiguration(forPaywall: paywall)
} catch {
print("Failed to load paywall: \(error)")
}
}
}
```
```swift
class ViewController: UIViewController {
private var paywallConfiguration: AdaptyUI.PaywallConfiguration?
override func viewDidLoad() {
super.viewDidLoad()
Task {
await initializePaywall()
}
}
private func initializePaywall() async {
do {
paywallConfiguration = try await loadPaywall()
if let paywallConfiguration {
await MainActor.run {
presentPaywall(with: paywallConfiguration)
}
}
} catch {
print("Error initializing paywall: \(error)")
}
}
private func loadPaywall() async throws -> AdaptyUI.PaywallConfiguration? {
let paywall = try await Adapty.getPaywall("YOUR_PLACEMENT_ID")
guard paywall.hasViewConfiguration else {
print("Paywall doesn't have view configuration")
return nil
}
return try await AdaptyUI.getPaywallConfiguration(forPaywall: paywall)
}
private func presentPaywall(with config: AdaptyUI.PaywallConfiguration) {
let paywallController = AdaptyUI.paywallController(with: config, delegate: self)
present(paywallController, animated: true)
}
}
// MARK: - AdaptyPaywallControllerDelegate
extension ViewController: AdaptyPaywallControllerDelegate {
func paywallController(_ controller: AdaptyPaywallController,
didPerform action: AdaptyUI.Action) {
switch action {
case let .close:
controller.dismiss(animated: true)
break
}
}
func paywallController(_ controller: AdaptyUI.PaywallController,
didFailPurchase product: AdaptyPaywallProduct,
error: AdaptyError) {
print("Purchase failed for \(product.vendorProductId): \(error)")
guard error.adaptyErrorCode != .paymentCancelled else {
return // Don't show alert for user cancellation
}
let message = switch error.adaptyErrorCode {
case .paymentNotAllowed:
"Purchases are not allowed on this device."
default:
"Purchase failed. Please try again."
}
let alert = UIAlertController(title: "Purchase Error", message: message, preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default) { _ in }
alert.addAction(okAction)
present(alert, animated: true)
}
func paywallController(_ controller: AdaptyUI.PaywallController,
didFinishRestore profile: AdaptyProfile) {
print("Restore finished successfully")
controller.dismiss(animated: true)
}
func paywallController(_ controller: AdaptyUI.PaywallController,
didFailRestore error: AdaptyError) {
print("Restore failed: \(error)")
}
func paywallController(_ controller: AdaptyUI.PaywallController,
didFailRendering error: AdaptyError) {
print("Rendering failed: \(error)")
controller.dismiss(animated: true)
}
}
```
---
# File: ios-check-subscription-status
---
---
title: "Comprobar el estado de suscripción en el SDK de iOS"
description: "Aprende a comprobar el estado de suscripción en tu app de iOS con Adapty."
---
Para decidir si los usuarios pueden acceder al contenido de pago o ver un paywall, necesitas comprobar su [nivel de acceso](access-level) en el perfil.
Este artículo te muestra cómo acceder al estado del perfil para decidir qué deben ver los usuarios: si mostrarles un paywall o concederles acceso a las funciones de pago.
## Obtener el estado de suscripción \{#get-subscription-status\}
Cuando decides si mostrar un paywall o contenido de pago a un usuario, compruebas su [nivel de acceso](access-level) en su perfil. Tienes dos opciones:
- Llamar a `getProfile` si necesitas los datos más recientes del perfil de inmediato (como al iniciar la app) o quieres forzar una actualización.
- Configurar **actualizaciones automáticas del perfil** para mantener una copia local que se actualice automáticamente cada vez que cambie el estado de la suscripción.
:::important
Por defecto, el nivel de acceso `premium` ya existe en Adapty. Si no necesitas configurar más de un nivel de acceso, puedes usar simplemente `premium`.
:::
### Obtener el perfil \{#get-profile\}
La forma más sencilla de obtener el estado de suscripción es usar el método `getProfile` para acceder al perfil:
```swift showLineNumbers
do {
let profile = try await Adapty.getProfile()
if profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive ?? false {
// grant access to premium features
}
} catch {
// handle the error
}
```
```swift showLineNumbers
Adapty.getProfile { result in
if let profile = try? result.get() {
// check the access
profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive ?? false {
// grant access to premium features
}
}
}
```
### Escuchar actualizaciones de suscripción \{#listen-to-subscription-updates\}
Si quieres recibir actualizaciones del perfil automáticamente en tu app:
1. Conforma el protocolo `AdaptyDelegate` en el tipo que prefieras e implementa el método `didLoadLatestProfile`. Adapty llamará a este método automáticamente cada vez que cambie el estado de suscripción del usuario. En el ejemplo siguiente usamos un tipo `SubscriptionManager` para gestionar los flujos de suscripción y el perfil del usuario. Este tipo puede inyectarse como dependencia o configurarse como singleton en una app UIKit, o añadirse al entorno SwiftUI desde la estructura principal de la app.
2. Guarda los datos del perfil actualizado cuando se llame a este método, para poder usarlos en toda tu app sin necesidad de realizar peticiones de red adicionales.
```swift
class SubscriptionManager: AdaptyDelegate {
nonisolated func didLoadLatestProfile(_ profile: AdaptyProfile) {
let hasAccess = profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive ?? false
// Update UI, unlock content, etc.
}
}
// Set delegate after Adapty activation
Adapty.delegate = subscriptionManager
```
:::note
Adapty llama automáticamente a `didLoadLatestProfile` cuando se inicia tu app, proporcionando datos de suscripción en caché aunque el dispositivo esté sin conexión.
:::
## Conectar el perfil con la lógica del paywall \{#connect-profile-with-paywall-logic\}
Cuando necesitas tomar decisiones inmediatas sobre mostrar paywalls o conceder acceso a funciones de pago, puedes comprobar el perfil del usuario directamente. Este enfoque es útil en situaciones como el inicio de la app, al entrar en secciones premium o antes de mostrar contenido específico.
```swift
private func checkAccessLevel() async -> Bool {
do {
let profile = try await Adapty.getProfile()
return profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive ?? false
} catch {
print("Error checking access level: \(error)")
return false
}
}
// In your initialization logic:
let hasAccess = await checkAccessLevel()
if !hasAccess {
paywallPresented = true // Show paywall if no access
}
```
```swift
private func checkAccessLevel() async throws -> Bool {
let profile = try await Adapty.getProfile()
return profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive ?? false
}
// In your initialization logic:
let hasAccess = try await checkAccessLevel()
if !hasAccess {
presentPaywall(with: paywallConfiguration)
}
```
## Próximos pasos \{#next-steps\}
Ahora que sabes cómo seguir el estado de suscripción, [aprende a trabajar con perfiles de usuario](ios-quickstart-identify) para asegurarte de que se integra con tu sistema de autenticación existente y los permisos de acceso compartido de pago.
Si no tienes tu propio sistema de autenticación, no es ningún problema: Adapty gestionará los usuarios por ti, aunque puedes leer la [guía](ios-quickstart-identify) para entender cómo funciona Adapty con usuarios anónimos.
---
# File: ios-quickstart-identify
---
---
title: "Identificar usuarios en el SDK de iOS"
description: "Guía de inicio rápido para configurar Adapty en la gestión de suscripciones in-app."
---
:::important
Esta guía es para ti si tienes tu propio sistema de autenticación. Aquí aprenderás a trabajar con perfiles de usuario en Adapty para que se alinee con tu sistema de autenticación existente.
:::
La forma de gestionar las compras de los usuarios depende del modelo de autenticación de tu app:
- Si tu app no usa autenticación backend y no almacena datos de usuario, consulta la [sección sobre usuarios anónimos](#anonymous-users).
- Si tu app tiene (o tendrá) autenticación backend, consulta la [sección sobre usuarios identificados](#identified-users).
**Conceptos clave**:
- Los **perfiles** son las entidades que necesita el SDK para funcionar. Adapty los crea automáticamente.
- Pueden ser anónimos **(sin customer user ID)** o identificados **(con customer user ID)**.
- Proporcionas el **customer user ID** para cruzar los perfiles de Adapty con tu sistema de autenticación interno.
Estas son las diferencias entre usuarios anónimos e identificados:
| | Usuarios anónimos | Usuarios identificados |
|------------------------------|----------------------------------------------------------------|-------------------------------------------------------------------------------------|
| **Gestión de compras** | Restauración de compras a nivel de store | Mantiene el historial de compras entre dispositivos mediante su customer user ID |
| **Gestión de perfiles** | Nuevos perfiles en cada reinstalación | El mismo perfil entre sesiones y dispositivos |
| **Persistencia de datos** | Los datos de usuarios anónimos están ligados a la instalación | Los datos de usuarios identificados persisten entre instalaciones de la app |
## Usuarios anónimos \{#anonymous-users\}
Si no tienes autenticación backend, **no necesitas gestionar la autenticación en el código de la app**:
1. Cuando el SDK se activa en el primer inicio de la app, Adapty **crea un nuevo perfil para el usuario**.
2. Cuando el usuario realiza una compra en la app, esta se **asocia con su perfil de Adapty y su cuenta del store**.
3. Cuando el usuario **reinstala** la app o la instala en un **nuevo dispositivo**, Adapty **crea un nuevo perfil anónimo al activarse**.
4. Si el usuario ya ha realizado compras en tu app, por defecto, sus compras se sincronizan automáticamente desde el App Store al activarse el SDK.
:::note
Las restauraciones desde copia de seguridad se comportan de forma diferente a las reinstalaciones. Por defecto, cuando un usuario restaura desde una copia de seguridad, el SDK conserva los datos en caché y no crea un nuevo perfil. Puedes configurar este comportamiento con el ajuste `clearDataOnBackup`. [Más información](sdk-installation-ios#clear-data-on-backup-restore).
:::
Con usuarios anónimos se crearán nuevos perfiles en cada instalación, pero eso no es un problema porque, en los análisis de Adapty, puedes [configurar qué se considerará una nueva instalación](general#4-installs-definition-for-analytics).
Para los usuarios anónimos, debes contar las instalaciones por **ID de dispositivo**. En este caso, cada instalación de la app en un dispositivo se cuenta como una instalación, incluidas las reinstalaciones.
## Usuarios identificados \{#identified-users\}
Tienes dos opciones para identificar usuarios en la app:
- [**Durante el inicio de sesión/registro:**](#during-loginsignup) Si los usuarios inician sesión después de que arranque tu app, llama a `identify()` con un customer user ID cuando se autentiquen.
- [**Durante la activación del SDK:**](#during-the-sdk-activation) Si ya tienes un customer user ID almacenado cuando se inicia la app, envíalo al llamar a `activate()`.
:::important
Por defecto, cuando Adapty recibe una compra de un Customer User ID que actualmente está asociado a otro Customer User ID, el nivel de acceso se comparte, de modo que ambos perfiles tienen acceso de pago. Puedes configurar este ajuste para transferir el acceso de pago de un perfil a otro o desactivar el uso compartido por completo. Consulta el [artículo](general#6-sharing-paid-access-between-user-accounts) para más detalles.
:::
### Durante el inicio de sesión/registro \{#during-loginsignup\}
Si identificas a los usuarios después del inicio de la app (por ejemplo, tras iniciar sesión o registrarse), usa el método `identify` para establecer su customer user ID.
- Si **no has usado este customer user ID antes**, Adapty lo vinculará automáticamente al perfil actual.
- Si **ya has usado este customer user ID para identificar al usuario**, Adapty pasará a trabajar con el perfil asociado a ese customer user ID.
:::important
Los customer user IDs deben ser únicos para cada usuario. Si defines el valor del parámetro de forma fija, todos los usuarios se considerarán como uno solo.
:::
Siempre usa `await` con `identify` antes de llamar a otros métodos del SDK. Las llamadas concurrentes producen `#3006 profileWasChanged` o recaen sobre el perfil anónimo. Consulta [Orden de llamadas en el SDK de iOS](ios-sdk-call-order).
```swift showLineNumbers
do {
try await Adapty.identify("YOUR_USER_ID") // Unique for each user
} catch {
// handle the error
}
```
```swift showLineNumbers
// User IDs must be unique for each user
Adapty.identify("YOUR_USER_ID") { error in
if let error {
// handle the error
}
}
```
### Durante la activación del SDK \{#during-the-sdk-activation\}
Si ya conoces un customer user ID cuando activas el SDK, puedes enviarlo en el método `activate` en lugar de llamar a `identify` por separado.
Si conoces un customer user ID pero lo estableces solo después de la activación, eso significará que, al activarse, Adapty creará un nuevo perfil anónimo y pasará al existente solo después de que llames a `identify`.
Puedes pasar un customer user ID existente (uno que hayas usado antes) o uno nuevo. Si pasas uno nuevo, el nuevo perfil creado al activarse se vinculará automáticamente al customer user ID.
:::note
Por defecto, la creación de perfiles anónimos no afecta a los dashboards de análisis, porque las instalaciones se cuentan según los IDs de dispositivo.
Un ID de dispositivo representa una única instalación de la app desde el store en un dispositivo y se regenera solo después de que la app se reinstale.
No depende de si es una primera instalación o una repetida, ni de si se usa un customer user ID existente.
Crear un perfil (al activar el SDK o cerrar sesión), iniciar sesión o actualizar la app sin reinstalarla no genera eventos de instalación adicionales.
Si quieres contar instalaciones basándote en usuarios únicos en lugar de dispositivos, ve a **App settings** y configura [**Installs definition for analytics**](general#4-installs-definition-for-analytics).
:::
```swift showLineNumbers
// Place in the app main struct for SwiftUI or in AppDelegate for UIKit
let configurationBuilder =
AdaptyConfiguration
.builder(withAPIKey: "PUBLIC_SDK_KEY")
.with(customerUserId: "YOUR_USER_ID") // Customer user IDs must be unique for each user. If you hardcode the parameter value, all users will be considered as one.
do {
try await Adapty.activate(with: configurationBuilder.build())
} catch {
// handle the error
}
```
```swift showLineNumbers
// Place in the app main struct for SwiftUI or in AppDelegate for UIKit
let configurationBuilder =
AdaptyConfiguration
.builder(withAPIKey: "PUBLIC_SDK_KEY")
.with(customerUserId: "YOUR_USER_ID") // Customer user IDs must be unique for each user. If you hardcode the parameter value, all users will be considered as one.
Adapty.activate(with: configurationBuilder.build()) { error in
// handle the error
}
```
### Cerrar sesión de usuarios \{#log-users-out\}
Si tienes un botón para que los usuarios cierren sesión, usa el método `logout`.
:::important
Cerrar la sesión de un usuario crea un nuevo perfil anónimo para ese usuario.
:::
```swift showLineNumbers
do {
try await Adapty.logout()
} catch {
// handle the error
}
```
```swift showLineNumbers
Adapty.logout { error in
if error == nil {
// successful logout
}
}
```
:::info
Para volver a iniciar sesión en la app, usa el método `identify`.
:::
### Permitir compras sin inicio de sesión \{#allow-purchases-without-login\}
Si tus usuarios pueden realizar compras tanto antes como después de iniciar sesión en tu app, debes asegurarte de que mantendrán el acceso después de iniciar sesión:
1. Cuando un usuario sin sesión iniciada realiza una compra, Adapty la vincula a su ID de perfil anónimo.
2. Cuando el usuario inicia sesión en su cuenta, Adapty pasa a trabajar con su perfil identificado.
- Si es un customer user ID nuevo (por ejemplo, la compra se realizó antes del registro), Adapty asigna el customer user ID al perfil actual, por lo que se mantiene todo el historial de compras.
- Si es un customer user ID existente (el customer user ID ya está vinculado a un perfil), debes obtener el nivel de acceso actual después del cambio de perfil. Puedes llamar a [`getProfile`](ios-check-subscription-status) justo después de la identificación, o [escuchar las actualizaciones del perfil](ios-check-subscription-status) para que los datos se sincronicen automáticamente.
## Próximos pasos \{#next-steps\}
¡Enhorabuena! Has implementado la lógica de pago in-app en tu app. ¡Te deseamos todo lo mejor con la monetización!
Para sacar aún más partido a Adapty, puedes explorar estos temas:
- [**Testing**](test-purchases-in-sandbox): Comprueba que todo funciona como se espera
- [**Onboardings**](ios-onboardings): Engancha a los usuarios con onboardings e impulsa la retención
- [**Integraciones**](configuration): Integra con servicios de atribución de marketing y análisis con una sola línea de código
- [**Establecer atributos de perfil personalizados**](setting-user-attributes): Añade atributos personalizados a los perfiles de usuario y crea segmentos para lanzar pruebas A/B o mostrar diferentes paywalls a distintos usuarios
---
# File: adapty-cursor
---
---
title: "Integra Adapty en tu app iOS con ayuda de IA"
description: "Una guía paso a paso para integrar Adapty en tu app iOS usando Cursor, Context7, ChatGPT, Claude u otras herramientas de IA."
---
Esta página cubre dos formas de integrar Adapty en tu app iOS. Usa la skill de integración del SDK que encontrarás a continuación para un flujo automatizado de extremo a extremo, o sigue el tutorial manual más abajo.
## Usa la skill de integración del SDK (beta) \{#use-the-sdk-integration-skill-beta\}
La [skill adapty-sdk-integration](https://github.com/adaptyteam/adapty-sdk-integration-skill) automatiza la integración de principio a fin: configuración del dashboard, instalación del SDK, paywall y verificación por etapas. El tutorial manual que encontrarás más abajo es la alternativa si tu herramienta no admite el formato Claude Skills.
**Herramientas compatibles**: Claude Code, GitHub Copilot CLI, OpenAI Codex, Gemini CLI.
### Instalar \{#install\}
Elige el comando para tu herramienta. La lista completa está en el [README de la skill](https://github.com/adaptyteam/adapty-sdk-integration-skill).
- **Claude Code**: Ejecuta `claude plugin marketplace add adaptyteam/adapty-sdk-integration-skill` y luego `claude plugin install adapty-sdk-integration@adapty` desde tu shell.
- **GitHub Copilot CLI**: Ejecuta `gh skill install adaptyteam/adapty-sdk-integration-skill`.
- **Gemini CLI**: Ejecuta `gemini skills install https://github.com/adaptyteam/adapty-sdk-integration-skill`.
- **OpenAI Codex u otra herramienta**: Clona el repositorio y copia `plugins/adapty-sdk-integration/skills/adapty-sdk-integration/` en el directorio de skills de tu herramienta.
### Ejecutar \{#run\}
En tu proyecto, ejecuta `/adapty-sdk-integration`. La skill detecta tu plataforma y hace algunas preguntas de configuración. A continuación, guía la configuración del dashboard, la instalación del SDK, el paywall y la verificación — obteniendo la documentación relevante de Adapty en cada etapa.
:::note
La skill está en beta. Si se detiene o se comporta de forma inesperada, el tutorial manual de más abajo cubre cada etapa paso a paso.
:::
## Antes de empezar: configuración del dashboard \{#before-you-start-dashboard-setup\}
Adapty requiere algo de configuración en el dashboard antes de escribir cualquier código del SDK. Puedes hacerlo con una skill de LLM interactiva o manualmente desde el Dashboard.
### Enfoque con skill (recomendado) \{#skill-approach-recommended\}
La skill de la CLI de Adapty permite que tu LLM configure tu app, productos, niveles de acceso, paywalls y placements directamente, sin necesidad de abrir el Dashboard en cada paso. Solo necesitas [conectar tu store](integrate-payments) en el Dashboard.
```
npx skills add adaptyteam/adapty-cli --skill adapty-cli
```
Una vez añadida la skill, ejecuta `/adapty-cli` en tu agente. Te guiará por cada paso, incluido cuándo abrir el Dashboard para conectar tu store.
### Enfoque manual desde el Dashboard \{#dashboard-approach\}
Si prefieres configurarlo todo manualmente, esto es lo que necesitas antes de escribir cualquier código. Tu LLM no puede consultar los valores del dashboard por ti — tendrás que proporcionárselos.
1. **Conecta tu store**: En el Adapty Dashboard, ve a **App settings → General**. Esto es obligatorio para que las compras funcionen.
[Conectar App Store](integrate-payments)
2. **Copia tu clave pública del SDK**: En el Adapty Dashboard, ve a **App settings → General** y localiza la sección **API keys**. En el código, es la cadena que pasas a `Adapty.activate("YOUR_PUBLIC_SDK_KEY")`.
3. **Crea al menos un producto**: En el Adapty Dashboard, ve a la página **Products**. No referenciarás los productos directamente en el código — Adapty los entrega a través de paywalls.
[Añadir productos](quickstart-products)
4. **Crea un paywall y un placement**: En el Adapty Dashboard, crea un paywall en la página **Paywalls** y asígnalo a un placement en la página **Placements**. En el código, el ID del placement es la cadena que pasas a `Adapty.getPaywall("YOUR_PLACEMENT_ID")`.
[Crear paywall](quickstart-paywalls)
5. **Configura los niveles de acceso**: En el Adapty Dashboard, configúralos por producto en la página **Products**. En el código, la cadena que se comprueba en `profile.accessLevels["premium"]`. El nivel de acceso `premium` predeterminado funciona para la mayoría de las apps. Si los usuarios de pago acceden a funciones diferentes según el producto (por ejemplo, un plan `basic` frente a un plan `pro`), [crea niveles de acceso adicionales](assigning-access-level-to-a-product) antes de empezar a programar.
:::tip
Una vez que tengas los cinco, estás listo para escribir código. Dile a tu LLM: "Mi clave pública del SDK es X, mi ID de placement es Y" para que pueda generar el código correcto de inicialización y obtención del paywall.
:::
### Configura cuando estés listo \{#set-up-when-ready\}
Esto no es obligatorio para empezar a programar, pero lo necesitarás a medida que tu integración madure:
- **Pruebas A/B**: Configúralas en la página **Placements**. No requieren cambios en el código.
[Pruebas A/B](ab-tests)
- **Paywalls y placements adicionales**: Añade más llamadas a `getPaywall` con diferentes IDs de placement.
- **Integraciones de analíticas**: Configúralas en la página **Integrations**. La configuración varía según la integración. Consulta las [integraciones de analíticas](analytics-integration) y las [integraciones de atribución](attribution-integration).
## Proporciona documentación de Adapty a tu LLM \{#feed-adapty-docs-to-your-llm\}
### Usa Context7 (recomendado) \{#use-context7-recommended\}
[Context7](https://context7.com) es un servidor MCP que da a tu LLM acceso directo a la documentación actualizada de Adapty. Tu LLM obtiene automáticamente la documentación adecuada según lo que le preguntes — sin necesidad de pegar URLs manualmente.
Context7 funciona con **Cursor**, **Claude Code**, **Windsurf** y otras herramientas compatibles con MCP. Para configurarlo, ejecuta:
```
npx ctx7 setup
```
Esto detecta tu editor y configura el servidor Context7. Para la configuración manual, consulta el [repositorio de Context7 en GitHub](https://github.com/upstash/context7).
Una vez configurado, haz referencia a la librería de Adapty en tus prompts:
```
Use the adaptyteam/adapty-docs library to look up how to install the iOS SDK
```
:::warning
Aunque Context7 elimina la necesidad de pegar enlaces de documentación manualmente, el orden de implementación es importante. Sigue el [tutorial de implementación](#implementation-walkthrough) que encontrarás más abajo paso a paso para asegurarte de que todo funciona correctamente.
:::
### Usa la documentación en texto plano \{#use-plain-text-docs\}
Puedes acceder a cualquier página de la documentación de Adapty como Markdown en texto plano. Añade `.md` al final de su URL o haz clic en **Copy for LLM** bajo el título del artículo. Por ejemplo: [adapty-cursor.md](https://adapty.io/docs/es/adapty-cursor.md).
Cada etapa del [tutorial de implementación](#implementation-walkthrough) incluye un bloque "Envía esto a tu LLM" con enlaces `.md` para pegar.
Para obtener más documentación a la vez, consulta los [archivos índice y los subconjuntos por plataforma](#plain-text-doc-index-files) más abajo.
## Tutorial de implementación \{#implementation-walkthrough\}
El resto de esta guía recorre la integración de Adapty en orden de implementación. Cada etapa incluye la documentación que debes enviar a tu LLM, qué deberías ver al terminar y los problemas más frecuentes.
### Planifica tu integración \{#plan-your-integration\}
Antes de empezar a escribir código, pide a tu LLM que analice tu proyecto y cree un plan de implementación. Si tu herramienta de IA tiene un modo de planificación (como el modo plan de Cursor o Claude Code), úsalo para que el LLM pueda leer tanto la estructura de tu proyecto como la documentación de Adapty antes de escribir cualquier código.
Dile a tu LLM qué enfoque usas para las compras — esto afecta a las guías que debe seguir:
- [**Paywall Builder de Adapty**](adapty-paywall-builder): Creas paywalls en el editor sin código de Adapty y el SDK los renderiza automáticamente.
- [**Paywalls creados manualmente**](making-purchases): Construyes tu propia UI del paywall en el código, pero sigues usando Adapty para obtener productos y gestionar compras.
- [**Modo Observer**](observer-vs-full-mode): Mantienes tu infraestructura de compras existente y usas Adapty solo para analíticas e integraciones.
¿No sabes cuál elegir? Lee la [tabla comparativa en el inicio rápido](ios-quickstart-paywalls).
### Instala y configura el SDK \{#install-and-configure-the-sdk\}
Instala el paquete del SDK de Adapty a través de Swift Package Manager en Xcode y actívalo con tu clave pública del SDK. Esta es la base — nada más funciona sin esto.
**Guía:** [Instalar y configurar el SDK de Adapty](sdk-installation-ios)
Envía esto a tu LLM:
```
Read these Adapty docs before writing code:
- https://adapty.io/docs/es/sdk-installation-ios.md
```
:::tip[Punto de control]
- **Resultado esperado:** La app compila y se ejecuta. La consola de Xcode muestra el log de activación de Adapty.
- **Problema frecuente:** "Public API key is missing" → comprueba que has reemplazado el marcador de posición con tu clave real de App settings.
:::
### Muestra paywalls y gestiona compras \{#show-paywalls-and-handle-purchases\}
Obtén un paywall por ID de placement, muéstralo y gestiona los eventos de compra. Las guías que necesitas dependen de cómo gestionas las compras.
Prueba cada compra en el sandbox a medida que avanzas — no esperes al final. Consulta [Probar compras en sandbox](test-purchases-in-sandbox) para las instrucciones de configuración.
**Guías:**
- [Habilitar compras con paywalls (inicio rápido)](ios-quickstart-paywalls)
- [Obtener paywalls del Paywall Builder y su configuración](get-pb-paywalls)
- [Mostrar paywalls](ios-present-paywalls)
- [Gestionar eventos del paywall](ios-handling-events)
- [Responder a acciones de botones](handle-paywall-actions)
Envía esto a tu LLM:
```
Read these Adapty docs before writing code:
- https://adapty.io/docs/es/ios-quickstart-paywalls.md
- https://adapty.io/docs/es/get-pb-paywalls.md
- https://adapty.io/docs/es/ios-present-paywalls.md
- https://adapty.io/docs/es/ios-handling-events.md
- https://adapty.io/docs/es/handle-paywall-actions.md
```
:::tip[Punto de control]
- **Resultado esperado:** El paywall aparece con los productos configurados. Al pulsar un producto se activa el diálogo de compra en sandbox.
- **Problema frecuente:** Paywall vacío o error en `getPaywall` → verifica que el ID del placement coincide exactamente con el del dashboard y que el placement tiene una audiencia asignada.
:::
**Guías:**
- [Habilitar compras en tu paywall personalizado (inicio rápido)](ios-quickstart-manual)
- [Obtener paywalls y productos](fetch-paywalls-and-products)
- [Renderizar un paywall diseñado con Remote Config](present-remote-config-paywalls)
- [Realizar compras](making-purchases)
- [Restaurar compras](restore-purchase)
Envía esto a tu LLM:
```
Read these Adapty docs before writing code:
- https://adapty.io/docs/es/ios-quickstart-manual.md
- https://adapty.io/docs/es/fetch-paywalls-and-products.md
- https://adapty.io/docs/es/present-remote-config-paywalls.md
- https://adapty.io/docs/es/making-purchases.md
- https://adapty.io/docs/es/restore-purchase.md
```
:::tip[Punto de control]
- **Resultado esperado:** Tu paywall personalizado muestra los productos obtenidos desde Adapty. Al pulsar un producto se activa el diálogo de compra en sandbox.
- **Problema frecuente:** Array de productos vacío → verifica que el paywall tiene productos asignados en el dashboard y que el placement tiene una audiencia.
:::
**Guías:**
- [Descripción general del modo Observer](observer-vs-full-mode)
- [Implementar el modo Observer](implement-observer-mode)
- [Reportar transacciones en modo Observer](report-transactions-observer-mode)
Envía esto a tu LLM:
```
Read these Adapty docs before writing code:
- https://adapty.io/docs/es/observer-vs-full-mode.md
- https://adapty.io/docs/es/implement-observer-mode.md
- https://adapty.io/docs/es/report-transactions-observer-mode.md
```
:::tip[Punto de control]
- **Resultado esperado:** Tras una compra en sandbox usando tu flujo de compra existente, la transacción aparece en el **Event Feed** del dashboard de Adapty.
- **Problema frecuente:** Sin eventos → verifica que estás reportando las transacciones a Adapty y que las notificaciones del servidor de App Store están configuradas.
:::
### Comprueba el estado de la suscripción \{#check-subscription-status\}
Tras una compra, comprueba el perfil del usuario para detectar un nivel de acceso activo y restringir el contenido premium.
**Guía:** [Comprobar el estado de la suscripción](ios-check-subscription-status)
Envía esto a tu LLM:
```
Read these Adapty docs before writing code:
- https://adapty.io/docs/es/ios-check-subscription-status.md
```
:::tip[Punto de control]
- **Resultado esperado:** Tras una compra en sandbox, `profile.accessLevels["premium"]?.isActive` devuelve `true`.
- **Problema frecuente:** `accessLevels` vacío tras la compra → comprueba que el producto tiene un nivel de acceso asignado en el dashboard.
:::
### Identifica a los usuarios \{#identify-users\}
Vincula las cuentas de usuario de tu app a los perfiles de Adapty para que las compras persistan entre dispositivos.
:::important
Omite este paso si tu app no tiene autenticación.
:::
**Guía:** [Identificar usuarios](ios-quickstart-identify)
Envía esto a tu LLM:
```
Read these Adapty docs before writing code:
- https://adapty.io/docs/es/ios-quickstart-identify.md
```
:::tip[Punto de control]
- **Resultado esperado:** Tras llamar a `Adapty.identify("your-user-id")`, la sección **Profiles** del dashboard muestra tu ID de usuario personalizado.
- **Problema frecuente:** Llama a `identify` después de la activación pero antes de obtener paywalls para evitar una atribución anónima del perfil.
:::
### Prepárate para el lanzamiento \{#prepare-for-release\}
Una vez que tu integración funcione en el sandbox, repasa la lista de verificación de lanzamiento para asegurarte de que todo está listo para producción.
**Guía:** [Lista de verificación de lanzamiento](release-checklist)
Envía esto a tu LLM:
```
Read these Adapty docs before releasing:
- https://adapty.io/docs/es/release-checklist.md
```
:::tip[Punto de control]
- **Resultado esperado:** Todos los elementos de la lista confirmados: conexión al store, notificaciones del servidor, flujo de compra, comprobaciones del nivel de acceso y requisitos de privacidad.
- **Problema frecuente:** Notificaciones del servidor de App Store no configuradas → configúralas en **App settings → iOS SDK** o los eventos no aparecerán en el dashboard.
:::
## Archivos índice de documentación en texto plano \{#plain-text-doc-index-files\}
Si necesitas dar a tu LLM un contexto más amplio más allá de páginas individuales, disponemos de archivos índice que listan o combinan toda la documentación de Adapty:
- [`llms.txt`](https://adapty.io/docs/es/llms.txt): Lista todas las páginas con enlaces `.md`. Un [estándar emergente](https://llmstxt.org/) para hacer los sitios web accesibles a los LLMs. Ten en cuenta que para algunos agentes de IA (p. ej., ChatGPT) tendrás que descargar `llms.txt` y subirlo al chat como archivo.
- [`llms-full.txt`](https://adapty.io/docs/es/llms-full.txt): Toda la documentación del sitio de Adapty combinada en un único archivo. Muy grande — úsalo solo cuando necesites el panorama completo.
- Subconjuntos específicos para iOS: [`ios-llms.txt`](https://adapty.io/docs/es/ios-llms.txt) e [`ios-llms-full.txt`](https://adapty.io/docs/es/ios-llms-full.txt): subconjuntos por plataforma que ahorran tokens en comparación con el sitio completo.
---
# File: get-pb-paywalls
---
---
title: "Obtener paywalls del Paywall Builder y su configuración en el SDK de iOS"
description: "Aprende cómo recuperar paywalls de PB en Adapty para un mejor control de suscripciones en tu app de iOS."
---
Después de [diseñar la parte visual de tu paywall](adapty-paywall-builder) con el nuevo Paywall Builder en el Adapty Dashboard, puedes mostrarlo en tu app móvil. El primer paso es obtener el paywall asociado al placement y su configuración de vista, tal como se describe a continuación.
:::warning
El nuevo Paywall Builder funciona con la versión 3.0 o superior del SDK de iOS. Para mostrar paywalls en Adapty SDK v2 diseñados con el Paywall Builder heredado, consulta [Mostrar paywalls diseñados con el Paywall Builder heredado](adapty-paywall-builder).
:::
Ten en cuenta que este tema hace referencia a los paywalls personalizados con Paywall Builder. Si estás implementando tus paywalls de forma manual, consulta el tema [Obtener paywalls y productos para paywalls con Remote Config en tu app móvil](fetch-paywalls-and-products).
:::tip
¿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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas.
:::
Antes de empezar a mostrar paywalls en tu app móvil (haz clic para expandir)
1. [Crea tus productos](create-product) en el Adapty Dashboard.
2. [Crea un paywall e incorpora los productos en él](create-paywall) en el Adapty Dashboard.
3. [Crea placements e incorpora tu paywall en ellos](create-placement) en el Adapty Dashboard.
4. Instala el [SDK de Adapty](sdk-installation-ios) en tu app móvil.
## Obtener un paywall diseñado con Paywall Builder \{#fetch-paywall-designed-with-paywall-builder\}
Si has [diseñado un paywall con Paywall Builder](adapty-paywall-builder), no necesitas preocuparte por renderizarlo en el código de tu app para mostrárselo al usuario. Ese tipo de paywall contiene tanto qué se debe mostrar dentro del paywall como cómo debe mostrarse. Aun así, necesitas obtener su ID a través del placement, su configuración de vista y luego presentarlo en tu app móvil.
Para garantizar un rendimiento óptimo, es fundamental obtener el paywall y su [configuración de vista](get-pb-paywalls#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder) lo antes posible, dejando tiempo suficiente para que las imágenes se descarguen antes de mostrárselas al usuario.
Para obtener un paywall, usa el método `getPaywall`:
```swift showLineNumbers
do {
let paywall = try await Adapty.getPaywall("YOUR_PLACEMENT_ID")
// the requested paywall
} catch {
// handle the error
}
```
```swift showLineNumbers
Adapty.getPaywall(placementId: "YOUR_PLACEMENT_ID", locale: "en") { result in
switch result {
case let .success(paywall):
// the requested paywall
case let .failure(error):
// handle the error
}
}
```
Parámetros:
| Parámetro | Presencia | Descripción |
|---------|--------|-----------|
| **placementId** | obligatorio | El identificador del [Placement](placements) deseado. Es el valor que especificaste al crear un placement en el Adapty Dashboard. |
| **locale** |
opcional
por defecto: `en`
|
El identificador de la [localización del paywall](add-paywall-locale-in-adapty-paywall-builder). Se espera que este parámetro sea un código de idioma compuesto por una o dos subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta corresponde al idioma y la segunda a la región.
Ejemplo: `en` significa inglés, `pt-br` representa el portugués brasileño.
Consulta [Localizaciones y códigos de idioma](localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos utilizarlos.
|
| **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` |
Por defecto, el SDK intentará cargar datos desde el servidor y devolverá los datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que tus usuarios siempre obtengan los datos más actualizados.
Sin embargo, si crees que tus usuarios tienen una conexión a internet inestable, considera usar `.returnCacheDataElseLoad` para devolver datos en caché si existen. En ese caso, los usuarios podrían no obtener los datos más recientes, pero experimentarán tiempos de carga más rápidos, independientemente de lo inestable que sea su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar solicitudes de red.
Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra cuando se desinstala la app o mediante una limpieza manual.
El SDK de Adapty almacena los paywalls localmente en dos capas: la caché actualizada regularmente descrita anteriormente y los [paywalls de respaldo](fallback-paywalls). También usamos CDN para obtener los paywalls más rápido y un servidor de respaldo independiente en caso de que la CDN no sea accesible. Este sistema está diseñado para garantizar que siempre obtengas la versión más reciente de tus paywalls, asegurando la fiabilidad incluso cuando la conexión a internet es escasa.
|
| **loadTimeout** | por defecto: 5 seg |
Este valor limita el tiempo de espera para este método. Si se alcanza el tiempo de espera, se devolverán los datos en caché o el respaldo local.
Ten en cuenta que en casos excepcionales este método puede superar ligeramente el tiempo especificado en `loadTimeout`, ya que la operación puede estar compuesta por diferentes solicitudes internamente.
|
Parámetros de respuesta:
| Parámetro | Descripción |
| :-------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Paywall | Un objeto [`AdaptyPaywall`](https://swift.adapty.io/documentation/adapty/adaptypaywall) con una lista de IDs de producto, el identificador del paywall, Remote Config y otras propiedades. |
## Obtener la configuración de vista de un paywall diseñado con Paywall Builder \{#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder\}
:::important
Asegúrate de activar el toggle **Show on device** en el paywall builder. Si esta opción no está activada, la configuración de vista no estará disponible para recuperarse.
:::
Después de obtener el paywall, comprueba si incluye una configuración de vista, lo que indica que fue creado con Paywall Builder. Esto te guiará sobre cómo mostrar el paywall. Si la configuración de vista está presente, trátalo como un paywall de Paywall Builder; si no, [trátalo como un paywall con Remote Config](present-remote-config-paywalls).
Usa el método `getPaywallConfiguration` para cargar la configuración de vista.
```swift showLineNumbers
guard paywall.hasViewConfiguration else {
// use your custom logic
return
}
do {
let paywallConfiguration = try await AdaptyUI.getPaywallConfiguration(
forPaywall: paywall,
products: products
)
// use loaded configuration
} catch {
// handle the error
}
```
Parámetros:
| Parámetro | Presencia | Descripción |
| :----------------------- | :------------- | :----------------------------------------------------------- |
| **paywall** | obligatorio | Un objeto `AdaptyPaywall` para obtener un controlador para el paywall deseado. |
| **loadTimeout** | por defecto: 5 seg | Este valor limita el tiempo de espera para este método. Si se alcanza el tiempo de espera, se devolverán los datos en caché o el respaldo local. Ten en cuenta que en casos excepcionales este método puede superar ligeramente el tiempo especificado en `loadTimeout`, ya que la operación puede estar compuesta por diferentes solicitudes internamente. |
| **products** | opcional | Proporciona un array de `AdaptyPaywallProducts` para optimizar el tiempo de visualización de los productos en pantalla. Si se pasa `nil`, AdaptyUI obtendrá automáticamente los productos necesarios. |
:::note
Si usas varios idiomas, aprende cómo añadir una [localización de Paywall Builder](add-paywall-locale-in-adapty-paywall-builder) y cómo usar los códigos de idioma correctamente [aquí](localizations-and-locale-codes).
:::
Una vez cargado, [presenta el paywall](ios-present-paywalls).
## Obtener un paywall para la audiencia predeterminada para cargarlo más rápido \{#get-a-paywall-for-a-default-audience-to-fetch-it-faster\}
Normalmente, los paywalls se obtienen casi al instante, así que no necesitas preocuparte por acelerar este proceso. Sin embargo, si tienes muchas audiencias y paywalls y tus usuarios tienen una conexión a internet débil, obtener un paywall puede tardar más de lo deseable. En esos casos, puede que quieras mostrar un paywall predeterminado para garantizar una experiencia de usuario fluida en lugar de no mostrar ningún paywall.
Para solucionar esto, puedes usar el método `getPaywallForDefaultAudience`, que obtiene el paywall del placement especificado para la audiencia **All Users**. Sin embargo, es fundamental entender que el enfoque recomendado es obtener el paywall con el método `getPaywall`, como se detalla en la sección [Obtener información del paywall](get-pb-paywalls#fetch-paywall-designed-with-paywall-builder) anterior.
:::warning
Por qué recomendamos usar `getPaywall`
El método `getPaywallForDefaultAudience` tiene algunos inconvenientes importantes:
- **Posibles problemas de compatibilidad con versiones anteriores**: Si necesitas mostrar paywalls diferentes para distintas versiones de la app (actual y futuras), puedes encontrarte con dificultades. Tendrás que diseñar paywalls compatibles con la versión actual (heredada) o aceptar que los usuarios con la versión actual (heredada) puedan tener problemas con paywalls que no se renderizan.
- **Pérdida de targeting**: Todos los usuarios verán el mismo paywall diseñado para la audiencia **All Users**, lo que significa que perderás el targeting personalizado (incluyendo por países, atribución de marketing o tus propios atributos personalizados).
Si estás dispuesto a aceptar estos inconvenientes para beneficiarte de una obtención más rápida del paywall, usa el método `getPaywallForDefaultAudience` como se indica a continuación. De lo contrario, usa `getPaywall` descrito [anteriormente](get-pb-paywalls#fetch-paywall-designed-with-paywall-builder).
:::
```swift showLineNumbers
Adapty.getPaywallForDefaultAudience(placementId: "YOUR_PLACEMENT_ID", locale: "en") { result in
switch result {
case let .success(paywall):
// the requested paywall
case let .failure(error):
// handle the error
}
}
```
:::note
El método `getPaywallForDefaultAudience` está disponible a partir de la versión 2.11.2 del SDK de iOS.
:::
| Parámetro | Presencia | Descripción |
|---------|--------|-----------|
| **placementId** | obligatorio | El identificador del [Placement](placements). Es el valor que especificaste al crear un placement en tu Adapty Dashboard. |
| **locale** |
opcional
por defecto: `en`
|
El identificador de la [localización del paywall](add-remote-config-locale). Se espera que este parámetro sea un código de idioma compuesto por una o más subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta corresponde al idioma y la segunda a la región.
Ejemplo: `en` significa inglés, `pt-br` representa el portugués brasileño.
Consulta [Localizaciones y códigos de idioma](localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos utilizarlos.
|
| **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` |
Por defecto, el SDK intentará cargar datos desde el servidor y devolverá los datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que tus usuarios siempre obtengan los datos más actualizados.
Sin embargo, si crees que tus usuarios tienen una conexión a internet inestable, considera usar `.returnCacheDataElseLoad` para devolver datos en caché si existen. En ese caso, los usuarios podrían no obtener los datos más recientes, pero experimentarán tiempos de carga más rápidos, independientemente de lo inestable que sea su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar solicitudes de red.
Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra cuando se desinstala la app o mediante una limpieza manual.
|
## Personalizar recursos \{#customize-assets\}
Para personalizar imágenes y vídeos en tu paywall, implementa los recursos personalizados.
Las imágenes y vídeos principales tienen IDs predefinidos: `hero_image` y `hero_video`. En un bundle de recursos personalizados, puedes apuntar a estos elementos por sus IDs y personalizar su comportamiento.
Para otras imágenes y vídeos, necesitas [establecer un ID personalizado](custom-media) en el dashboard de Adapty.
Por ejemplo, puedes:
- Mostrar una imagen o vídeo diferente a algunos usuarios.
- Mostrar una imagen de vista previa local mientras se carga la imagen principal remota.
- Mostrar una imagen de vista previa antes de reproducir un vídeo.
:::important
Para usar esta función, actualiza el SDK de Adapty para iOS a la versión 3.7.0 o superior.
:::
Aquí tienes un ejemplo de cómo proporcionar recursos personalizados mediante un diccionario simple:
```swift showLineNumbers
let customAssets: [String: AdaptyCustomAsset] = [
// Show a local image using a custom ID
"custom_image": .image(
.uiImage(value: UIImage(named: "image_name")!)
),
// Show a local preview image while a remote main image is loading
"hero_image": .image(
.remote(
url: URL(string: "https://example.com/image.jpg")!,
preview: UIImage(named: "preview_image")
)
),
// Show a local video with a preview image
"hero_video": .video(
.file(
url: Bundle.main.url(forResource: "custom_video", withExtension: "mp4")!,
preview: .uiImage(value: UIImage(named: "video_preview")!)
)
),
]
let paywallConfig = try await AdaptyUI.getPaywallConfiguration(
forPaywall: paywall,
assetsResolver: customAssets
)
```
:::note
Si no se encuentra un recurso, el paywall usará su apariencia predeterminada.
:::
## Configurar temporizadores definidos por el desarrollador \{#set-up-developer-defined-timers\}
Para usar temporizadores personalizados en tu app móvil, crea un objeto que siga el protocolo `AdaptyTimerResolver`. Este objeto define cómo debe renderizarse cada temporizador personalizado. Si lo prefieres, puedes usar directamente un diccionario `[String: Date]`, ya que ya cumple con este protocolo. Aquí tienes un ejemplo:
```swift showLineNumbers
@MainActor
struct AdaptyTimerResolverImpl: AdaptyTimerResolver {
func timerEndAtDate(for timerId: String) -> Date {
switch timerId {
case "CUSTOM_TIMER_6H":
Date(timeIntervalSinceNow: 3600.0 * 6.0) // 6 hours
case "CUSTOM_TIMER_NY":
Calendar.current.date(from: DateComponents(year: 2025, month: 1, day: 1)) ?? Date(timeIntervalSinceNow: 3600.0)
default:
Date(timeIntervalSinceNow: 3600.0) // 1 hour
}
}
}
```
En este ejemplo, `CUSTOM_TIMER_NY` y `CUSTOM_TIMER_6H` son los **Timer ID** de los temporizadores definidos por el desarrollador que configuraste en el Adapty Dashboard. El `timerResolver` garantiza que tu app actualice dinámicamente cada temporizador con el valor correcto. Por ejemplo:
- `CUSTOM_TIMER_NY`: El tiempo restante hasta el final del temporizador, como el día de Año Nuevo.
- `CUSTOM_TIMER_6H`: El tiempo restante en un período de 6 horas que comenzó cuando el usuario abrió el paywall.
---
# File: ios-present-paywalls
---
---
title: "Presentar paywalls del nuevo Paywall Builder en el SDK de iOS"
description: "Descubre cómo presentar paywalls en iOS para aumentar las conversiones y los ingresos."
---
Si has personalizado un paywall con el Paywall Builder, no necesitas preocuparte por renderizarlo en el código de tu app para mostrárselo al usuario. Ese paywall ya contiene tanto lo que debe mostrarse como la forma en que debe hacerlo.
:::warning
Esta guía es para **[paywalls del nuevo Paywall Builder](adapty-paywall-builder)**. El proceso para presentar paywalls varía según la versión del Paywall Builder con la que se diseñaron, si son paywalls de Remote Config o si usas el [modo Observer](observer-vs-full-mode).
- Para presentar **paywalls de Remote Config**, consulta [Renderizar paywall diseñado con Remote Config](present-remote-config-paywalls).
- Para presentar **paywalls en modo Observer**, consulta [iOS - Presentar paywalls del Paywall Builder en modo Observer](ios-present-paywall-builder-paywalls-in-observer-mode)
:::
Para obtener el objeto `AdaptyUI.PaywallConfiguration` que se usa a continuación, consulta [Obtener paywalls del Paywall Builder y su configuración](get-pb-paywalls).
## Presentar paywalls en SwiftUI \{#present-paywalls-in-swiftui\}
### Presentar como vista modal \{#present-as-a-modal-view\}
Para mostrar el paywall visual en la pantalla del dispositivo como vista modal, usa el modificador `.paywall` en SwiftUI:
```swift showLineNumbers title="SwiftUI"
@State var paywallPresented = false // ensure that you manage this variable state and set it to `true` at the moment you want to show the paywall
var body: some View {
Text("Hello, AdaptyUI!")
.paywall(
isPresented: $paywallPresented,
paywallConfiguration: ,
didPerformAction: { action in
switch action {
case .close:
paywallPresented = false
default:
// Handle other actions
break
}
},
didFinishPurchase: { product, profile in paywallPresented = false },
didFailPurchase: { product, error in /* handle the error */ },
didFinishRestore: { profile in /* check access level and dismiss */ },
didFailRestore: { error in /* handle the error */ },
didFailRendering: { error in paywallPresented = false }
)
}
```
Parámetros:
| Parámetro | Requerido | Descripción |
|:----------------------------------|:---------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **isPresented** | requerido | Un binding que controla si la pantalla del paywall está visible. |
| **paywallConfiguration** | requerido | Un objeto `AdaptyUI.PaywallConfiguration` con los detalles visuales del paywall. Usa el método `AdaptyUI.paywallConfiguration(for:products:viewConfiguration:observerModeResolver:tagResolver:timerResolver:)`. Consulta [Obtener paywalls del Paywall Builder y su configuración](get-pb-paywalls) para más detalles. |
| **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. |
| **didFailRendering** | requerido | Se invoca si ocurre un error al renderizar la interfaz. En ese caso, [contacta con el soporte de Adapty](mailto:support@adapty.io). |
| **fullScreen** | opcional | Determina si el paywall se muestra en pantalla completa o como modal. Por defecto es `true`. |
| **didAppear** | opcional | Se invoca cuando la vista del paywall se presenta. |
| **didDisappear** | opcional | Se invoca cuando la vista del paywall se descarta. |
| **didPerformAction** | opcional | Se invoca cuando el usuario pulsa un botón. Cada botón tiene un ID de acción distinto. Hay dos IDs predefinidos: `close` y `openURL`; los demás son personalizados y se pueden configurar en el builder. |
| **didSelectProduct** | opcional | Se invoca cuando se selecciona un producto para la compra (por el usuario o por el sistema). |
| **didStartPurchase** | opcional | Se invoca cuando el usuario inicia el proceso de compra. |
| **didFinishPurchase** | opcional | Se invoca cuando `Adapty.makePurchase()` finaliza correctamente. |
| **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 ocurren errores al cargar los productos. Devuelve `true` para reintentar la carga. |
| **didPartiallyLoadProducts** | opcional | Se invoca cuando los productos se cargan de forma parcial. |
| **showAlertItem** | opcional | Un binding que gestiona la visualización de alertas 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. |
Consulta el tema [iOS - Gestión de eventos](ios-handling-events) para más detalles sobre los parámetros.
### Presentar como vista no modal \{#present-as-a-non-modal-view\}
También puedes presentar paywalls como destinos de navegación o vistas en línea dentro del flujo de navegación de tu app. Usa `AdaptyPaywallView` directamente en tus vistas SwiftUI:
```swift showLineNumbers title="SwiftUI"
AdaptyPaywallView(
paywallConfiguration: ,
didFailPurchase: { product, error in
// Handle purchase failure
},
didFinishRestore: { profile in
// Handle successful restore
},
didFailRestore: { error in
// Handle restore failure
},
didFailRendering: { error in
// Handle rendering error
}
)
```
## Presentar paywalls en UIKit \{#present-paywalls-in-uikit\}
Para mostrar el paywall visual en la pantalla del dispositivo, sigue estos pasos:
1. Inicializa el paywall visual que quieres mostrar usando el método `.paywallController(for:products:viewConfiguration:delegate:)`:
```swift showLineNumbers title="Swift"
import Adapty
import AdaptyUI
let visualPaywall = AdaptyUI.paywallController(
with: ,
delegate:
)
```
Parámetros de la solicitud:
| Parámetro | Presencia | Descripción |
| :----------------------- | :------- | :---------- |
| **paywall configuration** | requerido | Un objeto `AdaptyUI.PaywallConfiguration` con los detalles visuales del paywall. Usa el método `AdaptyUI.getPaywallConfiguration(forPaywall:locale:)`. Consulta [Obtener paywalls del Paywall Builder y su configuración](get-pb-paywalls) para más detalles. |
| **delegate** | requerido | Un `AdaptyPaywallControllerDelegate` para escuchar los eventos del paywall. Consulta el tema [Gestión de eventos del paywall](ios-handling-events) para más detalles.
Devuelve:
| Objeto | Descripción |
| :---------------------- | :--------------------------------------------------- |
| **AdaptyPaywallController** | Un objeto que representa la pantalla del paywall solicitado |
2. Una vez creado el objeto correctamente, puedes mostrarlo en la pantalla del dispositivo:
```swift showLineNumbers title="Swift"
present(visualPaywall, animated: true)
```
:::tip
¿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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas.
:::
---
# File: handle-paywall-actions
---
---
title: "Responder a acciones de botones en el SDK de iOS"
description: "Gestiona las acciones de botones de paywall en iOS usando Adapty para una mejor monetización de tu app."
---
Si estás creando paywalls con el Paywall Builder de Adapty, es fundamental configurar los botones correctamente:
1. Añade un [botón en el paywall builder](paywall-buttons) y asígnale una acción predefinida o crea un ID de acción personalizado.
2. Escribe código en tu app para gestionar cada acción que hayas asignado.
Esta guía muestra cómo gestionar acciones personalizadas y predefinidas en tu código.
:::warning
**Solo las compras, restauraciones, cierres de paywall y apertura de URLs se gestionan automáticamente.** Todas las demás acciones de botones requieren una implementación adecuada en el código de la app.
:::
## Cerrar paywalls \{#close-paywalls\}
Para añadir un botón que cierre tu paywall:
1. En el paywall builder, añade un botón y asígnale la acción **Close**.
2. En el código de tu app, implementa un handler para la acción `close` que descarte el paywall.
:::info
En el SDK de iOS, la acción `close` cierra el paywall por defecto. Sin embargo, puedes sobrescribir este comportamiento en tu código si lo necesitas. Por ejemplo, cerrar un paywall podría desencadenar la apertura de otro.
:::
```swift
func paywallController(_ controller: AdaptyPaywallController,
didPerform action: AdaptyUI.Action) {
switch action {
case .close:
controller.dismiss(animated: true) // default behavior
break
}
}
```
## Abrir URLs desde paywalls \{#open-urls-from-paywalls\}
:::tip
Si quieres añadir un grupo de enlaces (por ejemplo, términos de uso y restauración de compras), añade un elemento **Link** en el paywall builder y trátalo igual que los botones con la acción **Open URL**.
:::
Para añadir un botón que abra un enlace desde tu paywall (por ejemplo, **Terms of use** o **Privacy policy**):
1. En el paywall builder, añade un botón, asígnale la acción **Open URL** e introduce la URL que quieres abrir.
2. En el código de tu app, implementa un handler para la acción `openUrl` que abra la URL recibida en un navegador.
:::info
En el SDK de iOS, la acción `openUrl` abre la URL por defecto. Sin embargo, puedes sobrescribir este comportamiento en tu código si lo necesitas.
:::
```swift
func paywallController(_ controller: AdaptyPaywallController,
didPerform action: AdaptyUI.Action) {
switch action {
case let .openURL(url):
UIApplication.shared.open(url, options: [:]) // default behavior
break
}
}
```
## Iniciar sesión en la app \{#log-into-the-app\}
Para añadir un botón que permita a los usuarios iniciar sesión en tu app:
1. En el paywall builder, añade un botón y asígnale la acción **Login**.
2. En el código de tu app, implementa un handler para la acción `login` que identifique a tu usuario.
```swift
func paywallController(_ controller: AdaptyPaywallController,
didPerform action: AdaptyUI.Action) {
switch action {
case .login:
// Show a login screen
let loginVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "LoginViewController")
controller.present(loginVC, animated: true)
}
}
```
## Gestionar acciones personalizadas \{#handle-custom-actions\}
Para añadir un botón que gestione cualquier otra acción:
1. En el paywall builder, añade un botón, asígnale la acción **Custom** y dale un ID.
2. En el código de tu app, implementa un handler para el ID de acción que hayas creado.
Por ejemplo, si tienes otro conjunto de ofertas de suscripción o compras únicas, puedes añadir un botón que muestre otro paywall:
```swift
func paywallController(_ controller: AdaptyPaywallController,
didPerform action: AdaptyUI.Action) {
switch action {
case let .custom(id):
if id == "openNewPaywall" {
// Display another paywall
}
}
break
}
}
```
---
# File: ios-handling-events
---
---
title: "Gestionar eventos del paywall en el SDK de iOS"
description: "Gestiona eventos relacionados con suscripciones en iOS usando Adapty para una mejor monetización de la app."
---
:::important
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](handle-paywall-actions) para más detalles.
:::
Los paywalls configurados con el [Paywall Builder](adapty-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.
:::tip
¿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](sample-apps), 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 \{#handling-events-in-swiftui\}
Para controlar o supervisar los procesos que ocurren en la pantalla del paywall dentro de tu app, usa el modificador `.paywall` en SwiftUI:
```swift showLineNumbers title="Swift"
@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á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](get-pb-paywalls) 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 por cancelaciones del usuario ni pagos pendientes. |
| **didFinishRestore** | requerido | Se invoca cuando la compra se completa correctamente. |
| **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](mailto:support@adapty.io). |
| **fullScreen** | opcional | Determina si el paywall aparece en pantalla completa o como modal. Por defecto 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](web-paywall#step-2a-add-a-web-purchase-button) 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](web-paywall#step-2a-add-a-web-purchase-button) abierto desde un paywall en un navegador in-app desaparece de la pantalla. |
| **didPerformAction** | opcional | Se 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. |
| **didSelectProduct** | opcional | Si se selecciona un producto para comprar (por el usuario o por el sistema), se invocará este callback. |
| **didStartPurchase** | opcional | Se invoca cuando el usuario inicia el proceso de compra. |
| **didFinishPurchase** | opcional | Se invoca cuando la compra se completa correctamente. |
| **didFinishWebPaymentNavigation** | opcional | Se invoca tras intentar abrir un [web paywall](web-paywall) para realizar una compra, tanto si tiene éxito como si falla. |
| **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. |
## Gestionar eventos en UIKit \{#handling-events-in-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 \{#user-generated-events\}
#### Selección de producto \{#product-selection\}
Si el usuario selecciona un producto para comprar, se invocará este método:
```swift showLineNumbers title="Swift"
func paywallController(
_ controller: AdaptyPaywallController,
didSelectProduct product: AdaptyPaywallProductWithoutDeterminingOffer
) { }
```
Ejemplo de evento (haz clic para expandir)
```javascript
{
"product": {
"vendorProductId": "premium_monthly",
"localizedTitle": "Premium Monthly",
"localizedDescription": "Premium subscription for 1 month",
"localizedPrice": "$9.99",
"price": 9.99,
"currencyCode": "USD"
}
}
```
#### Compra iniciada \{#started-purchase\}
Si el usuario inicia el proceso de compra, se invocará este método:
```swift showLineNumbers title="Swift"
func paywallController(_ controller: AdaptyPaywallController,
didStartPurchase product: AdaptyPaywallProduct) {
}
```
Ejemplo de evento (haz clic para expandir)
```javascript
{
"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](ios-present-paywall-builder-paywalls-in-observer-mode) para más detalles.
#### Compra iniciada mediante un web paywall \{#started-purchase-using-a-web-paywall\}
Si el usuario inicia el proceso de compra usando un [web paywall](web-paywall), se invocará este método:
```swift showLineNumbers title="Swift"
func paywallController(
_ controller: AdaptyPaywallController,
shouldContinueWebPaymentNavigation product: AdaptyPaywallProduct
) {
}
```
Ejemplo de evento (haz clic para expandir)
```javascript
{
"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 \{#successful-or-canceled-purchase\}
Si la compra tiene éxito, se invocará este método:
```swift showLineNumbers title="Swift"
func paywallController(
_ controller: AdaptyPaywallController,
didFinishPurchase product: AdaptyPaywallProductWithoutDeterminingOffer,
purchaseResult: AdaptyPurchaseResult
) { }
}
```
Ejemplos de evento (haz clic para expandir)
```javascript
// 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](ios-present-paywall-builder-paywalls-in-observer-mode) para más detalles.
#### Compra fallida \{#failed-purchase\}
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.
```swift showLineNumbers title="Swift"
func paywallController(
_ controller: AdaptyPaywallController,
didFailPurchase product: AdaptyPaywallProduct,
error: AdaptyError
) { }
```
Ejemplo de evento (haz clic para expandir)
```javascript
{
"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](ios-present-paywall-builder-paywalls-in-observer-mode) para más detalles.
#### Compra fallida mediante un web paywall \{#failed-purchase-using-a-web-paywall\}
Si `Adapty.openWebPaywall()` falla, se invocará este método:
```swift showLineNumbers title="Swift"
func paywallController(
_ controller: AdaptyPaywallController,
didFailWebPaymentNavigation product: AdaptyPaywallProduct,
error: AdaptyError
) { }
```
Ejemplo de evento (haz clic para expandir)
```javascript
{
"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 \{#successful-restore\}
Si la restauración de una compra tiene éxito, se invocará este método:
```swift showLineNumbers title="Swift"
func paywallController(
_ controller: AdaptyPaywallController,
didFinishRestoreWith profile: AdaptyProfile
) { }
```
Ejemplo de evento (haz clic para expandir)
```javascript
{
"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](subscription-status) para aprender a comprobarlo.
#### Restauración fallida \{#failed-restore\}
Si la restauración de una compra falla, se invocará este método:
```swift showLineNumbers title="Swift"
public func paywallController(
_ controller: AdaptyPaywallController,
didFailRestoreWith error: AdaptyError
) { }
```
Ejemplo de evento (haz clic para expandir)
```javascript
{
"error": {
"code": "restore_failed",
"message": "Purchase restoration failed",
"details": {
"underlyingError": "No previous purchases found"
}
}
}
```
### Obtención de datos y renderizado \{#data-fetching-and-rendering\}
#### Errores al cargar productos \{#product-loading-errors\}
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:
```swift showLineNumbers title="Swift"
public func paywallController(
_ controller: AdaptyPaywallController,
didFailLoadingProductsWith error: AdaptyError
) -> Bool {
return true
}
```
Ejemplo de evento (haz clic para expandir)
```javascript
{
"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 \{#rendering-errors\}
Si ocurre un error durante el renderizado de la interfaz, se notificará mediante este método:
```swift showLineNumbers title="Swift"
public func paywallController(
_ controller: AdaptyPaywallController,
didFailRenderingWith error: AdaptyError
) { }
```
Ejemplo de evento (haz clic para expandir)
```javascript
{
"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.
---
# File: ios-use-fallback-paywalls
---
---
title: "iOS - Usar paywalls de respaldo"
description: "Gestiona los casos en que los usuarios están sin conexión o los servidores de Adapty no están disponibles"
---
:::warning
Los paywalls de respaldo son compatibles con el SDK de iOS v2.11 o posterior.
:::
Para mantener una experiencia de usuario fluida, es importante configurar [respaldos](/fallback-paywalls) para tus [paywalls](paywalls) y [onboardings](onboardings). Esta precaución amplía las capacidades de la aplicación en caso de pérdida parcial o total de la conexión a internet.
* **Si la aplicación no puede acceder a los servidores de Adapty:**
Podrá mostrar un paywall de respaldo y acceder a la configuración local del onboarding.
* **Si la aplicación no puede acceder a internet:**
Podrá mostrar un paywall de respaldo. Los onboardings incluyen contenido remoto y requieren conexión a internet para funcionar.
:::important
Antes de seguir los pasos de esta guía, [descarga](/local-fallback-paywalls) los archivos de configuración de respaldo desde Adapty.
:::
## Configuración \{#configuration\}
1. Añade el archivo JSON de respaldo al bundle de tu proyecto: abre el menú **File** en XCode y selecciona la opción **Add Files to "YourProjectName"**.
2. Llama al método `.setFallback` **antes** de obtener el paywall o onboarding de destino.
```swift showLineNumbers
do {
if let urlPath = Bundle.main.url(forResource: fileName, withExtension: "json") {
try await Adapty.setFallback(fileURL: urlPath)
}
} catch {
// handle the error
}
```
```swift showLineNumbers
if let url = Bundle.main.url(forResource: "ios_fallback", withExtension: "json") {
Adapty.setFallback(fileURL: url)
}
```
Parámetros:
| Parámetro | Descripción |
| :---------- |:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **fileURL** | Ruta al archivo de configuración de respaldo. |
---
# File: localizations-and-locale-codes
---
---
title: "Usar localizaciones y códigos de idioma en el SDK de iOS"
description: "Gestiona las localizaciones de la app y los códigos de idioma para llegar a una audiencia global en tu app de iOS."
---
## Por qué esto es importante \{#why-this-is-important\}
Hay algunos escenarios en los que los códigos de idioma entran en juego, por ejemplo, cuando intentas obtener el paywall correcto para la localización actual de tu app.
Como los códigos de idioma son complejos y pueden variar de una plataforma a otra, nos basamos en un estándar interno para todas las plataformas que soportamos. Sin embargo, precisamente por esa complejidad, es muy importante que entiendas exactamente qué estás enviando a nuestro servidor para obtener la localización correcta y qué ocurre a continuación, de modo que siempre recibas lo que esperas.
## Estándar de códigos de idioma en Adapty \{#locale-code-standard-at-adapty\}
Para los códigos de idioma, Adapty utiliza una versión ligeramente modificada del [estándar BCP 47](https://en.wikipedia.org/wiki/IETF_language_tag): cada código está formado por subetiquetas en minúsculas separadas por guiones. Algunos ejemplos: `en` (inglés), `pt-br` (portugués de Brasil), `zh` (chino simplificado), `zh-hant` (chino tradicional).
## Coincidencia de códigos de idioma \{#locale-code-matching\}
Cuando Adapty recibe una llamada del SDK con el código de idioma y busca la localización correspondiente de un paywall, ocurre lo siguiente:
1. La cadena de idioma entrante se convierte a minúsculas y todos los guiones bajos (`_`) se reemplazan con guiones (`-`).
2. Buscamos la localización con el código de idioma que coincida exactamente.
3. Si no se encuentra ninguna coincidencia, tomamos la subcadena antes del primer guion (`pt` para `pt-br`) y buscamos la localización correspondiente.
4. Si tampoco se encuentra coincidencia, devolvemos la localización por defecto `en`.
De este modo, un dispositivo iOS que envíe `'pt_BR'`, un dispositivo Android que envíe `pt-BR` y otro dispositivo que envíe `pt-br` obtendrán el mismo resultado.
## Implementar localizaciones: la forma recomendada \{#implementing-localizations-recommended-way\}
Si te estás preguntando por las localizaciones, lo más probable es que ya estés trabajando con archivos de cadenas localizadas en tu proyecto. En ese caso, te recomendamos añadir un par clave-valor con el código de idioma de Adapty correspondiente en cada uno de tus archivos para las localizaciones respectivas, y luego extraer el valor de esa clave al llamar a nuestro SDK, como se muestra aquí:
```swift showLineNumbers
// 1. Modify your Localizable.strings files
/*
Localizable.strings - Spanish
*/
adapty_paywalls_locale = "es";
/*
Localizable.strings - Portuguese (Brazil)
*/
adapty_paywalls_locale = "pt-br";
// 2. Extract and use the locale code
let locale = NSLocalizedString("adapty_paywalls_locale", comment: "")
// pass locale code to AdaptyUI.getViewConfiguration or Adapty.getPaywall method
```
Así tienes el control total sobre qué localización se obtendrá para cada usuario de tu app.
## Implementar localizaciones: la otra forma \{#implementing-localizations-the-other-way\}
Puedes obtener resultados similares (aunque no idénticos) sin definir explícitamente los códigos de idioma para cada localización. Esto implica extraer un código de idioma de otros objetos que proporciona tu plataforma, como en este ejemplo:
```swift showLineNumbers
let locale = Locale.current.identifier
// pass locale code to AdaptyUI.getViewConfiguration or Adapty.getPaywall method
```
No recomendamos este enfoque por varios motivos:
1. En iOS, los idiomas preferidos y el idioma actual no son idénticos. Si quieres que la localización se seleccione correctamente, tendrás que apoyarte en la lógica de Apple, que funciona de manera automática si usas el enfoque recomendado con archivos de cadenas localizadas, o bien recrearla tú mismo.
2. Es difícil predecir exactamente qué recibirá el servidor de Adapty. Por ejemplo, en iOS es posible obtener un código como `ar_OM@numbers='latn'` en un dispositivo y enviarlo a nuestro servidor. Para esa llamada no obtendrás la localización `ar-om` que buscabas, sino `ar`, lo cual probablemente no es lo que esperabas.
Si aun así decides utilizar este enfoque, asegúrate de haber cubierto todos los casos de uso relevantes.
---
# File: ios-web-paywall
---
---
title: "Implementar web paywalls en el SDK de iOS"
description: "Configura un web paywall para cobrar sin las comisiones y revisiones de la App Store."
---
:::important
Antes de empezar, asegúrate de haber [configurado tu web paywall en el dashboard](web-paywall) y de tener instalada la versión 3.6.1 o posterior del SDK de Adapty.
:::
## Abrir web paywalls \{#open-web-paywalls\}
Si estás trabajando con un paywall que desarrollaste tú mismo, debes gestionar los web paywalls usando el método del SDK. El método `.openWebPaywall`:
1. Genera una URL única que permite a Adapty vincular un paywall específico mostrado a un usuario concreto con la página web a la que es redirigido.
2. Detecta cuando tus usuarios vuelven a la app y luego solicita `.getProfile` a intervalos cortos para determinar si los derechos de acceso del perfil se han actualizado.
De este modo, si el pago se ha completado correctamente y los derechos de acceso se han actualizado, la suscripción se activa en la app casi de inmediato.
```swift showLineNumbers title="Swift"
do {
try await Adapty.openWebPaywall(for: product)
} catch {
print("Failed to open web paywall: \(error)")
}
```
:::note
Existen dos versiones del método `openWebPaywall`:
1. `openWebPaywall(product)`, que genera URLs a partir del paywall y también añade los datos del producto a las URLs.
2. `openWebPaywall(paywall)`, que genera URLs a partir del paywall sin añadir los datos del producto. Úsalo cuando tus productos en el paywall de Adapty sean distintos de los del web paywall.
:::
## Gestionar errores \{#handle-errors\}
| Error | Descripción | Acción recomendada |
|-----------------------------------------|------------------------------------------------------------------|-------------------------------------------------------------------------------------------------|
| AdaptyError.paywallWithoutPurchaseUrl | El paywall no tiene configurada una URL de compra web | Comprueba si el paywall está correctamente configurado en el Adapty Dashboard |
| AdaptyError.productWithoutPurchaseUrl | El producto no tiene una URL de compra web | Verifica la configuración del producto en el Adapty Dashboard |
| AdaptyError.failedOpeningWebPaywallUrl | No se pudo abrir la URL en el navegador | Revisa la configuración del dispositivo o proporciona un método de compra alternativo |
| AdaptyError.failedDecodingWebPaywallUrl | No se pudieron codificar correctamente los parámetros de la URL | Verifica que los parámetros de la URL sean válidos y estén correctamente formateados |
## Ejemplo de implementación \{#implementation-example\}
```swift showLineNumbers title="Swift"
class SubscriptionViewController: UIViewController {
var paywall: AdaptyPaywall?
@IBAction func purchaseButtonTapped(_ sender: UIButton) {
guard let paywall = paywall, let product = paywall.products.first else { return }
Task {
await offerWebPurchase(for: product)
}
}
func offerWebPurchase(for paywallProduct: AdaptyPaywallProduct) async {
do {
// Attempt to open web paywall
try await Adapty.openWebPaywall(for: paywallProduct)
} catch let error as AdaptyError {
switch error {
case .paywallWithoutPurchaseUrl, .productWithoutPurchaseUrl:
showAlert(message: "Web purchase is not available for this product.")
case .failedOpeningWebPaywallUrl:
showAlert(message: "Could not open web browser. Please try again.")
default:
showAlert(message: "An error occurred: \(error.localizedDescription)")
}
} catch {
showAlert(message: "An unexpected error occurred.")
}
}
// Helper methods
private func showAlert(message: String) { /* ... */ }
}
```
:::note
Cuando los usuarios vuelvan a la app, actualiza la interfaz para reflejar los cambios del perfil. `AdaptyDelegate` recibirá y procesará los eventos de actualización del perfil.
:::
## Abrir web paywalls en un navegador in-app \{#open-web-paywalls-in-an-in-app-browser\}
:::important
La apertura de web paywalls en un navegador in-app es compatible a partir del SDK de Adapty v. 3.15.
:::
Por defecto, los web paywalls se abren en el navegador externo.
Para ofrecer una experiencia de usuario más fluida, puedes abrir los web paywalls en un navegador in-app. Esto muestra la página de compra web dentro de tu aplicación, permitiendo a los usuarios completar las transacciones sin cambiar de app.
Para activarlo, establece el parámetro `in` en `.inAppBrowser`:
```swift showLineNumbers title="Swift"
do {
try await Adapty.openWebPaywall(for: product, in: .inAppBrowser) // default – .externalBrowser
} catch {
print("Failed to open web paywall: \(error)")
}
```
---
# File: ios-troubleshoot-paywall-builder
---
---
title: "Solucionar problemas del Paywall Builder en el SDK de iOS"
description: "Solucionar problemas del Paywall Builder en el SDK de iOS"
---
Esta guía te ayuda a resolver los problemas más comunes al usar paywalls diseñados en el Adapty Paywall Builder con el SDK de iOS.
## Fallo al obtener la configuración del paywall \{#getting-a-paywall-configuration-fails\}
**Problema**: El método `getPaywallConfiguration` no consigue recuperar la configuración del paywall.
**Causa**: El paywall no está habilitado para mostrarse en el dispositivo en el Paywall Builder.
**Solución**: Activa el interruptor **Show on device** en el Paywall Builder.
## El número de vistas del paywall es demasiado alto \{#the-paywall-view-number-is-too-big\}
**Problema**: El contador de vistas del paywall muestra el doble del número esperado.
**Causa**: Es posible que estés llamando a `logShowPaywall` en tu código, lo que duplica el contador de vistas si usas el Paywall Builder. En los paywalls diseñados con el Paywall Builder, el seguimiento de analíticas es automático, por lo que no necesitas usar este método.
**Solución**: Asegúrate de no llamar a `logShowPaywall` en tu código si usas el Paywall Builder.
## Otros problemas \{#other-issues\}
**Problema**: Estás experimentando otros problemas relacionados con el Paywall Builder que no se tratan arriba.
**Solución**: Migra el SDK a la versión más reciente siguiendo las [guías de migración](ios-sdk-migration-guides) si es necesario. Muchos problemas se resuelven en versiones más nuevas del SDK.
---
# File: ios-quickstart-manual
---
---
title: "Habilitar compras en tu paywall personalizado en iOS SDK"
description: "Integra el SDK de Adapty en tus paywalls personalizados de iOS para habilitar compras in-app."
---
Esta guía describe cómo integrar Adapty en tus paywalls personalizados. Mantén el control total sobre la implementación del paywall, mientras el SDK de Adapty obtiene los productos, gestiona las nuevas compras y restaura las anteriores.
:::important
**Esta guía es para desarrolladores que implementan paywalls personalizados.** Si quieres la forma más sencilla de habilitar compras, usa el [Adapty Paywall Builder](ios-quickstart-paywalls). Con Paywall Builder, creas paywalls en un editor visual sin código, Adapty gestiona toda la lógica de compras automáticamente y puedes probar distintos diseños sin volver a publicar tu app.
:::
## Antes de empezar \{#before-you-start\}
### Configura los productos \{#set-up-products\}
Para habilitar las compras in-app, necesitas entender tres conceptos clave:
- [**Productos**](product) – cualquier cosa que los usuarios pueden comprar (suscripciones, consumibles, acceso de por vida)
- [**Paywalls**](paywalls) – configuraciones que definen qué productos ofrecer. En Adapty, los paywalls son la única forma de recuperar productos, pero este diseño te permite modificar productos, precios y ofertas sin tocar el código de tu app.
- [**Placements**](placements) – dónde y cuándo mostrar los paywalls en tu app (como `main`, `onboarding`, `settings`). Configuras los paywalls para los placements en el dashboard y luego los solicitas por ID de placement en tu código. Esto facilita ejecutar pruebas A/B y mostrar diferentes paywalls a distintos usuarios.
Asegúrate de entender estos conceptos aunque trabajes con tu paywall personalizado. Básicamente, son tu forma de gestionar los productos que vendes en tu app.
Para implementar tu paywall personalizado, necesitarás crear un **paywall** y añadirlo a un **placement**. Esta configuración te permite recuperar tus productos. Para entender qué tienes que hacer en el dashboard, sigue la guía de inicio rápido [aquí](quickstart).
### Gestión de usuarios \{#manage-users\}
Puedes trabajar con o sin autenticación backend en tu lado.
Sin embargo, el SDK de Adapty gestiona los usuarios anónimos e identificados de forma diferente. Lee la [guía de inicio rápido de identificación](ios-quickstart-identify) para entender los detalles y asegurarte de que estás trabajando con los usuarios correctamente.
## Paso 1. Obtener productos \{#step-1-get-products\}
Para recuperar productos para tu paywall personalizado, necesitas:
1. Obtener el objeto `paywall` pasando el ID del [placement](placements) al método `getPaywall`.
2. Obtener el array de productos para este paywall usando el método `getPaywallProducts`.
```swift
func loadPaywall() async {
do {
let paywall = try await Adapty.getPaywall("YOUR_PLACEMENT_ID")
let products = try await Adapty.getPaywallProducts(paywall: paywall)
// Use products to build your custom paywall UI
} catch {
// Handle the error
}
}
```
```swift
func loadPaywall() {
Adapty.getPaywall("YOUR_PLACEMENT_ID") { result in
switch result {
case let .success(paywall):
Adapty.getPaywallProducts(paywall: paywall) { result in
switch result {
case let .success(products):
// Use products to build your custom paywall UI
case let .failure(error):
// Handle the error
}
}
case let .failure(error):
// Handle the error
}
}
}
```
## Paso 2. Aceptar compras \{#step-2-accept-purchases\}
Cuando un usuario pulsa en un producto de tu paywall personalizado, llama al método `makePurchase` con el producto seleccionado. Esto gestionará el flujo de compra y devolverá el perfil actualizado.
```swift
func purchaseProduct(_ product: AdaptyPaywallProduct) async {
do {
let purchaseResult = try await Adapty.makePurchase(product: product)
switch purchaseResult {
case .userCancelled:
// User canceled the purchase
break
case .pending:
// Purchase is pending (e.g., awaiting parental approval)
break
case let .success(profile, transaction):
// Purchase successful, profile updated
break
}
} catch {
// Handle the error
}
}
```
```swift
func purchaseProduct(_ product: AdaptyPaywallProduct) {
Adapty.makePurchase(product: product) { result in
switch result {
case let .success(purchaseResult):
switch purchaseResult {
case .userCancelled:
// User canceled the purchase
break
case .pending:
// Purchase is pending (e.g., awaiting parental approval)
break
case let .success(profile, transaction):
// Purchase successful, profile updated
break
}
case let .failure(error):
// Handle the error
}
}
}
```
## Paso 3. Restaurar compras \{#step-3-restore-purchases\}
Apple exige que todas las apps con suscripciones ofrezcan una forma de que los usuarios puedan restaurar sus compras. Aunque las compras se restauran automáticamente cuando un usuario inicia sesión con su Apple ID, debes implementar igualmente un botón de restauración en tu app.
Llama al método `restorePurchases` cuando el usuario pulse el botón de restaurar. Esto sincronizará su historial de compras con Adapty y devolverá el perfil actualizado.
```swift
func restorePurchases() async {
do {
let profile = try await Adapty.restorePurchases()
// Restore successful, profile updated
} catch {
// Handle the error
}
}
```
```swift
func restorePurchases() {
Adapty.restorePurchases { result in
switch result {
case let .success(profile):
// Restore successful, profile updated
case let .failure(error):
// Handle the error
}
}
}
```
## Próximos pasos \{#next-steps\}
---
no_index: true
---
import Callout from '../../../components/Callout.astro';
¿Tienes preguntas o estás teniendo algún problema? Consulta nuestro [foro de soporte](https://adapty.featurebase.app/) donde encontrarás respuestas a preguntas frecuentes o podrás plantear las tuyas. ¡Nuestro equipo y la comunidad están aquí para ayudarte!
Tu paywall está listo para mostrarse en la app. [Prueba tus compras en modo sandbox](test-purchases-in-sandbox) para asegurarte de que puedes completar una compra de prueba desde el paywall.
A continuación, [comprueba si los usuarios han completado su compra](ios-check-subscription-status) para determinar si mostrar el paywall o conceder acceso a las funciones de pago.
---
# File: fetch-paywalls-and-products
---
---
title: "Obtener paywalls y productos para paywalls con Remote Config en iOS SDK"
description: "Obtén paywalls y productos en el SDK de Adapty para iOS y mejora la monetización."
---
Antes de mostrar paywalls con Remote Config y personalizados, necesitas obtener la información sobre ellos. Ten en cuenta que este tema se refiere a paywalls con Remote Config y personalizados. Para obtener información sobre cómo obtener paywalls creados con Paywall Builder, consulta las [iOS](get-pb-paywalls), [Android](android-get-pb-paywalls), [React Native](react-native-get-pb-paywalls), [Flutter](flutter-get-pb-paywalls), y [Unity](unity-get-pb-paywalls).
:::tip
¿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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas.
:::
Antes de empezar a obtener paywalls y productos en tu app (haz clic para expandir)
1. [Crea tus productos](create-product) en el Adapty Dashboard.
2. [Crea un paywall e incorpora los productos a tu paywall](create-paywall) en el Adapty Dashboard.
3. [Crea placements e incorpora tu paywall al placement](create-placement) en el Adapty Dashboard.
4. [Instala el SDK de Adapty](sdk-installation-ios) en tu app.
## Obtener información del paywall \{#fetch-paywall-information\}
En Adapty, un [producto](product) es una combinación de productos de App Store y Google Play. Estos productos multiplataforma se integran en paywalls, lo que te permite mostrarlos en placements específicos de tu app.
Para mostrar los productos, necesitas obtener un [Paywall](paywalls) de uno de tus [placements](placements) con el método `getPaywall`.
:::important
**No escribas los IDs de producto en el código.** El único ID que debes incluir directamente en el código es el ID del placement. Los paywalls se configuran de forma remota, por lo que el número de productos y las ofertas disponibles pueden cambiar en cualquier momento. Tu app debe manejar estos cambios de forma dinámica: si hoy un paywall devuelve dos productos y mañana tres, muéstralos todos sin necesidad de cambiar el código.
:::
```swift showLineNumbers
do {
let paywall = try await Adapty.getPaywall(placementId: "YOUR_PLACEMENT_ID")
// the requested paywall
} catch {
// handle the error
}
```
```swift showLineNumbers
Adapty.getPaywall(placementId: "YOUR_PLACEMENT_ID", locale: "en") { result in
switch result {
case let .success(paywall):
// the requested paywall
case let .failure(error):
// handle the error
}
}
```
| Parámetro | Presencia | Descripción |
|---------|--------|-----------|
| **placementId** | obligatorio | El identificador del [Placement](placements). Es el valor que especificaste al crear un placement en tu Adapty Dashboard. |
| **locale** |
opcional
por defecto: `en`
|
El identificador de la [localización del paywall](add-remote-config-locale). Se espera que este parámetro sea un código de idioma compuesto por una o más subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta corresponde al idioma y la segunda a la región.
Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.
Consulta [Localizaciones y códigos de idioma](localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos usarlos.
|
| **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` |
Por defecto, el SDK intentará cargar datos del servidor y devolverá datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que tus usuarios siempre obtengan los datos más actualizados.
Sin embargo, si crees que tus usuarios tienen una conexión a internet inestable, considera usar `.returnCacheDataElseLoad` para devolver datos en caché si existen. En este escenario, los usuarios podrían no obtener los datos más recientes, pero disfrutarán de tiempos de carga más rápidos, sin importar la calidad de su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar peticiones de red.
Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra cuando se reinstala la app o mediante limpieza manual.
El SDK de Adapty almacena los paywalls en dos capas: la caché actualizada regularmente descrita anteriormente y los [paywalls de respaldo](fallback-paywalls). También usamos CDN para obtener los paywalls más rápido y un servidor de respaldo independiente en caso de que el CDN no sea accesible. Este sistema está diseñado para garantizar que siempre obtengas la versión más reciente de tus paywalls, asegurando la fiabilidad incluso cuando la conexión a internet es escasa.
|
| **loadTimeout** | por defecto: 5 seg |
Este valor limita el tiempo de espera para este método. Si se alcanza el tiempo límite, se devolverán los datos en caché o el respaldo local.
Ten en cuenta que en casos excepcionales este método puede superar ligeramente el tiempo especificado en `loadTimeout`, ya que la operación puede estar compuesta por diferentes peticiones internamente.
|
¡No escribas los IDs de producto directamente en el código! Como los paywalls se configuran de forma remota, los productos disponibles, su número y las ofertas especiales (como pruebas gratuitas) pueden cambiar con el tiempo. Asegúrate de que tu código gestione estos escenarios.
Por ejemplo, si inicialmente obtienes 2 productos, tu app debería mostrar esos 2 productos. Sin embargo, si más adelante obtienes 3 productos, tu app debería mostrar los 3 sin necesidad de cambios en el código. Lo único que debes incluir directamente en el código es el ID del placement.
Parámetros de respuesta:
| Parámetro | Descripción |
| :-------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Paywall | Un objeto [`AdaptyPaywall`](https://swift.adapty.io/documentation/adapty/adaptypaywall) con: una lista de IDs de producto, el identificador del paywall, Remote Config y varias otras propiedades. |
## Obtener productos \{#fetch-products\}
Una vez que tienes el paywall, puedes consultar el array de productos que le corresponde:
```swift showLineNumbers
do {
let products = try await Adapty.getPaywallProducts(paywall: paywall)
// the requested products array
} catch {
// handle the error
}
```
```swift showLineNumbers
Adapty.getPaywallProducts(paywall: paywall) { result in
switch result {
case let .success(products):
// the requested products array
case let .failure(error):
// handle the error
}
}
```
Parámetros de respuesta:
| Parámetro | Descripción |
| :-------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Products | Lista de objetos [`AdaptyPaywallProduct`](https://swift.adapty.io/documentation/adapty/adaptypaywallproduct) con: identificador del producto, nombre del producto, precio, moneda, duración de la suscripción y varias otras propiedades. |
Al implementar tu propio diseño de paywall, probablemente necesitarás acceder a estas propiedades del objeto [`AdaptyPaywallProduct`](https://swift.adapty.io/documentation/adapty/adaptypaywallproduct). A continuación se muestran las propiedades más utilizadas, pero consulta el documento enlazado para ver todos los detalles sobre las propiedades disponibles.
| Propiedad | Descripción |
|-------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **Title** | Para mostrar el título del producto, usa `product.localizedTitle`. Ten en cuenta que la localización se basa en el país de la store seleccionado por el usuario, no en el idioma del dispositivo. |
| **Price** | Para mostrar una versión localizada del precio, usa `product.localizedPrice`. Esta localización se basa en la información de idioma del dispositivo. También puedes acceder al precio como número con `product.price`. El valor se proporcionará en la moneda local. Para obtener el símbolo de moneda asociado, usa `product.currencySymbol`. |
| **Subscription Period** | Para mostrar el período (por ejemplo, semana, mes, año, etc.), usa `product.localizedSubscriptionPeriod`. Esta localización se basa en el idioma del dispositivo. Para obtener el período de suscripción de forma programática, usa `product.subscriptionPeriod`. Desde ahí puedes acceder al enum `unit` para obtener la duración (es decir, día, semana, mes, año o desconocido). El valor `numberOfUnits` te dará el número de unidades del período. Por ejemplo, para una suscripción trimestral, verías `.month` en la propiedad unit y `3` en la propiedad numberOfUnits. |
| **Introductory Offer** | Para mostrar una insignia u otro indicador de que una suscripción incluye una oferta introductoria, consulta la propiedad `product.subscriptionOffer`. Dentro de este objeto se encuentran las siguientes propiedades útiles: • `offerType`: un enum con los valores `introductory`, `promotional` y `winBack`. Las pruebas gratuitas y las suscripciones con descuento inicial serán del tipo `introductory`. • `price`: El precio con descuento como número. Para las pruebas gratuitas, busca `0` aquí. • `localizedPrice`: Un precio formateado del descuento para el idioma del usuario. • `localizedNumberOfPeriods`: una cadena localizada según el idioma del dispositivo que describe la duración de la oferta. Por ejemplo, una oferta de prueba de tres días muestra `3 days` en este campo. • `subscriptionPeriod`: También puedes obtener los detalles individuales del período de la oferta con esta propiedad. Funciona de la misma manera para las ofertas que la sección anterior. • `localizedSubscriptionPeriod`: Un período de suscripción formateado del descuento para el idioma del usuario. |
## Comprobar la elegibilidad para la oferta introductoria en iOS \{#check-intro-offer-eligibility-on-ios\}
Por defecto, el método `getPaywallProducts` comprueba la elegibilidad para las ofertas introductorias, promocionales y de recuperación. Si necesitas mostrar productos antes de que el SDK determine la elegibilidad para las ofertas, usa el método `getPaywallProductsWithoutDeterminingOffer` en su lugar.
:::note
Después de mostrar los productos iniciales, asegúrate de llamar al método habitual `getPaywallProducts` para actualizar los productos con información precisa sobre la elegibilidad de las ofertas.
:::
```swift showLineNumbers
do {
let products = try await Adapty.getPaywallProductsWithoutDeterminingOffer(paywall: paywall)
// the requested products array without subscriptionOffer
} catch {
// handle the error
}
```
```swift showLineNumbers
Adapty.getPaywallProductsWithoutDeterminingOffer(paywall: paywall) { result in
switch result {
case let .success(products):
// the requested products array without subscriptionOffer
case let .failure(error):
// handle the error
}
}
```
## Acelera la obtención del paywall con el paywall de audiencia predeterminada \{#speed-up-paywall-fetching-with-default-audience-paywall\}
Normalmente, los paywalls se obtienen casi de forma instantánea, por lo que no necesitas preocuparte por acelerar este proceso. Sin embargo, cuando tienes muchas audiencias y paywalls, y tus usuarios tienen una conexión a internet débil, obtener un paywall puede tardar más de lo deseable. En esas situaciones, puede que quieras mostrar un paywall predeterminado para garantizar una experiencia fluida en lugar de no mostrar ninguno.
Para resolver esto, puedes usar el método `getPaywallForDefaultAudience`, que obtiene el paywall del placement especificado para la audiencia **All Users**. Sin embargo, es fundamental entender que el enfoque recomendado es obtener el paywall con el método `getPaywall`, tal como se detalla en la sección [Obtener información del paywall](fetch-paywalls-and-products#fetch-paywall-information) anterior.
:::warning
Por qué recomendamos usar `getPaywall`
El método `getPaywallForDefaultAudience` tiene algunos inconvenientes importantes:
- **Posibles problemas de compatibilidad con versiones anteriores**: Si necesitas mostrar paywalls diferentes para distintas versiones de la app (actuales y futuras), podrías enfrentarte a dificultades. Tendrás que diseñar paywalls que sean compatibles con la versión actual (legacy) o aceptar que los usuarios con esa versión puedan encontrarse con paywalls que no se renderizan correctamente.
- **Pérdida de segmentación**: Todos los usuarios verán el mismo paywall diseñado para la audiencia **All Users**, lo que significa que pierdes la segmentación personalizada (incluyendo por países, atribución de marketing o tus propios atributos personalizados).
Si estás dispuesto a aceptar estos inconvenientes para beneficiarte de una obtención más rápida del paywall, usa el método `getPaywallForDefaultAudience` como se indica a continuación. De lo contrario, utiliza el `getPaywall` descrito [anteriormente](fetch-paywalls-and-products#fetch-paywall-information).
:::
```swift showLineNumbers
do {
let paywall = try await Adapty.getPaywallForDefaultAudience("YOUR_PLACEMENT_ID")
// the requested paywall
} catch {
// handle the error
}
```
```swift showLineNumbers
Adapty.getPaywallForDefaultAudience(placementId: "YOUR_PLACEMENT_ID", locale: "en") { result in
switch result {
case let .success(paywall):
// the requested paywall
case let .failure(error):
// handle the error
}
}
```
:::note
El método `getPaywallForDefaultAudience` está disponible a partir de la versión 2.11.2 del SDK de iOS.
:::
| Parámetro | Presencia | Descripción |
|---------|--------|-----------|
| **placementId** | obligatorio | El identificador del [Placement](placements). Es el valor que especificaste al crear un placement en tu Adapty Dashboard. |
| **locale** |
opcional
por defecto: `en`
|
El identificador de la [localización del paywall](add-remote-config-locale). Se espera que este parámetro sea un código de idioma compuesto por una o más subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta corresponde al idioma y la segunda a la región.
Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.
Consulta [Localizaciones y códigos de idioma](localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos usarlos.
|
| **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` |
Por defecto, el SDK intentará cargar datos del servidor y devolverá datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que tus usuarios siempre obtengan los datos más actualizados.
Sin embargo, si crees que tus usuarios tienen una conexión a internet inestable, considera usar `.returnCacheDataElseLoad` para devolver datos en caché si existen. En este escenario, los usuarios podrían no obtener los datos más recientes, pero disfrutarán de tiempos de carga más rápidos, sin importar la calidad de su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar peticiones de red.
Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra cuando se reinstala la app o mediante limpieza manual.
|
---
# File: present-remote-config-paywalls
---
---
title: "Mostrar un paywall diseñado con Remote Config en iOS SDK"
description: "Descubre cómo presentar paywalls de Remote Config en Adapty para personalizar la experiencia del usuario."
---
Si has personalizado un paywall usando Remote Config, necesitarás implementar el renderizado en el código de tu app para mostrárselo a los usuarios. Como Remote Config ofrece flexibilidad adaptada a tus necesidades, tú decides qué incluir y cómo se ve tu paywall. Te proporcionamos un método para obtener la configuración remota, dándote autonomía para mostrar tu paywall personalizado configurado mediante Remote Config.
No olvides [comprobar si un usuario es elegible para una oferta introductoria en iOS](fetch-paywalls-and-products#check-intro-offer-eligibility-on-ios) y adaptar la vista del paywall para gestionar el caso en que sea elegible.
## Obtener el Remote Config del paywall y mostrarlo \{#get-paywall-remote-config-and-present-it\}
Para obtener el Remote Config de un paywall, accede a la propiedad `remoteConfig` y extrae los valores necesarios.
```swift showLineNumbers
do {
let paywall = try await Adapty.getPaywall(placementId: "YOUR_PLACEMENT_ID")
let headerText = paywall.remoteConfig?.dictionary?["header_text"] as? String
} catch {
// handle the error
}
```
```swift showLineNumbers
Adapty.getPaywall(placementId: "YOUR_PLACEMENT_ID") { result in
let paywall = try? result.get()
let headerText = paywall?.remoteConfig?.dictionary?["header_text"] as? String
}
```
En este punto, una vez que hayas recibido todos los valores necesarios, es momento de renderizarlos y ensamblarlos en una página visualmente atractiva. Asegúrate de que el diseño se adapte a las distintas pantallas y orientaciones de los móviles, ofreciendo una experiencia fluida y amigable en todos los dispositivos.
:::warning
Asegúrate de [registrar el evento de visualización del paywall](present-remote-config-paywalls#track-paywall-view-events) como se describe a continuación, para que los análisis de Adapty capturen la información necesaria para los embudos y las pruebas A/B.
:::
Una vez que hayas terminado de mostrar el paywall, continúa configurando el flujo de compra. Cuando el usuario realice una compra, simplemente llama a `.makePurchase()` con el producto de tu paywall. Para más información sobre el método `.makePurchase()`, consulta [Realizar compras](making-purchases).
Te recomendamos [crear un paywall de respaldo llamado fallback paywall](fallback-paywalls). Este respaldo se mostrará al usuario cuando no haya conexión a internet o caché disponible, garantizando una experiencia fluida incluso en esas situaciones.
## Registrar eventos de visualización del paywall \{#track-paywall-view-events\}
Adapty te ayuda a medir el rendimiento de tus paywalls. Aunque los datos de compras se recopilan automáticamente, registrar las visualizaciones del paywall requiere tu intervención, ya que solo tú sabes cuándo un usuario ve un paywall.
Para registrar un evento de visualización del paywall, simplemente llama a `.logShowPaywall(paywall)`, y quedará reflejado en las métricas de tu paywall en los embudos y las pruebas A/B.
:::important
No es necesario llamar a `.logShowPaywall(paywall)` si estás mostrando paywalls creados con el [Paywall Builder](adapty-paywall-builder).
:::
```swift showLineNumbers
Adapty.logShowPaywall(paywall)
```
Parámetros de la solicitud:
| Parámetro | Presencia | Descripción |
| :---------- | :------- |:-----------------------------------------------------------------------------------------|
| **paywall** | obligatorio | Un objeto [`AdaptyPaywall`](https://swift.adapty.io/documentation/adapty/adaptypaywall). |
---
# File: making-purchases
---
---
title: "Realizar compras in-app en iOS SDK"
description: "Guía para gestionar compras in-app y suscripciones con Adapty."
---
Mostrar paywalls en tu aplicación móvil es un paso esencial para ofrecer a los usuarios acceso a contenido o servicios premium. Sin embargo, con solo mostrar estos paywalls es suficiente para admitir compras únicamente si usas el [Paywall Builder](adapty-paywall-builder) para personalizar tus paywalls.
Si no usas el Paywall Builder, debes utilizar un método independiente llamado `.makePurchase()` para completar una compra y desbloquear el contenido deseado. Este método actúa como punto de entrada para que los usuarios interactúen con los paywalls y realicen sus transacciones.
Si tu paywall tiene una oferta promocional activa para el producto que el usuario intenta comprar, Adapty la aplicará automáticamente en el momento de la compra.
:::warning
Ten en cuenta que la oferta introductoria se aplicará automáticamente solo si usas paywalls configurados con el Paywall Builder.
En otros casos, tendrás que [verificar la elegibilidad del usuario para una oferta introductoria en iOS](fetch-paywalls-and-products#check-intro-offer-eligibility-on-ios). Omitir este paso puede provocar que tu app sea rechazada durante la publicación. Además, podría suponer cobrar el precio completo a usuarios que sí son elegibles para una oferta introductoria.
:::
Asegúrate de haber completado la [configuración inicial](quickstart) sin saltarte ningún paso. Sin ella, no podemos validar las compras.
## Realizar una compra \{#make-purchase\}
:::note
**¿Usas el [Paywall Builder](adapty-paywall-builder)?** Las compras se procesan automáticamente: puedes saltarte este paso.
**¿Buscas instrucciones paso a paso?** Consulta la [guía de inicio rápido](ios-implement-paywalls-manually) para ver instrucciones de implementación completas con todo el contexto.
:::
```swift showLineNumbers
do {
let purchaseResult = try await Adapty.makePurchase(product: product)
switch purchaseResult {
case .userCancelled:
// Handle the case where the user canceled the purchase
case .pending:
// Handle deferred purchases (e.g., the user will pay offline with cash)
case let .success(profile, transaction):
if profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive ?? false {
// Grant access to the paid features
}
}
} catch {
// Handle the error
}
```
```swift showLineNumbers
Adapty.makePurchase(product: product) { result in
switch result {
case let .success(purchaseResult):
switch purchaseResult {
case .userCancelled:
// Handle the case where the user canceled the purchase
case .pending:
// Handle deferred purchases (e.g., the user will pay offline with cash)
case let .success(profile, transaction):
if profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive ?? false {
// Grant access to the paid features
}
}
case let .failure(error):
// Handle the error
}
}
```
Parámetros de la solicitud:
| Parámetro | Obligatorio | Descripción |
| :---------- | :---------- | :-------------------------------------------------------------------------------------------------- |
| **Product** | obligatorio | Un objeto [`AdaptyPaywallProduct`](https://swift.adapty.io/documentation/adapty/adaptypaywallproduct) obtenido del paywall. |
Parámetros de la respuesta:
| Parámetro | Descripción |
|---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **Profile** |
Si la solicitud fue exitosa, la respuesta contiene este objeto. Un objeto [AdaptyProfile](https://swift.adapty.io/documentation/adapty/adaptyprofile) proporciona información completa sobre los niveles de acceso, suscripciones y compras únicas de un usuario dentro de la app.
Comprueba el estado del nivel de acceso para verificar si el usuario tiene el acceso requerido a la app.
|
:::warning
**Nota:** si todavía usas una versión de StoreKit de Apple inferior a v2.0 y una versión de Adapty SDK inferior a v.2.9.0, debes proporcionar el [secreto compartido de Apple App Store](app-store-connection-configuration#step-5-enter-app-store-shared-secret). Este método está actualmente deprecado por Apple.
:::
## Compras in-app desde el App Store \{#in-app-purchases-from-the-app-store\}
Cuando un usuario inicia una compra en el App Store y la transacción se traslada a tu app, tienes dos opciones:
- **Procesar la transacción de inmediato:** Devuelve `true` en `shouldAddStorePayment`. Esto mostrará la pantalla del sistema de compra de Apple de inmediato.
- **Guardar el objeto del producto para procesarlo más tarde:** Devuelve `false` en `shouldAddStorePayment` y después llama a `makePurchase` con el producto guardado cuando sea oportuno. Esto puede ser útil si necesitas mostrar algo personalizado al usuario antes de iniciar la compra.
Aquí tienes el código completo:
```swift showLineNumbers title="Swift"
final class YourAdaptyDelegateImplementation: AdaptyDelegate {
nonisolated func shouldAddStorePayment(for product: AdaptyDeferredProduct) -> Bool {
// 1a.
// Return `true` to continue the transaction in your app. The Apple purchase system screen will show automatically.
// 1b.
// Store the product object and return `false` to defer or cancel the transaction.
false
}
// 2. Continue the deferred purchase later on by passing the product to `makePurchase` when the timing is appropriate
func continueDeferredPurchase() async {
let storedProduct: AdaptyDeferredProduct = // get the product object from 1b.
do {
try await Adapty.makePurchase(product: storedProduct)
} catch {
// handle the error
}
}
}
```
## Canjear códigos de oferta en iOS \{#redeem-offer-codes-in-ios\}
---
no_index: true
---
import Callout from '../../../components/Callout.astro';
Sobre los códigos de oferta
Los códigos de oferta te permiten dar descuentos o períodos de prueba gratuitos a usuarios concretos. A diferencia de las ofertas habituales, que se aplican de forma automática, los códigos de oferta se distribuyen fuera de la app — por email, redes sociales o materiales impresos. Los usuarios los canjean introduciendo el código en el App Store, accediendo a una URL de canje o a través de un diálogo dentro de la app.
Para configurar códigos de oferta, abre una suscripción en App Store Connect y ve a su sección **Offer Codes**. Puedes crear [tres tipos](https://developer.apple.com/help/app-store-connect/manage-subscriptions/set-up-subscription-offer-codes) de códigos de oferta:
- **Free** — la suscripción es gratuita durante un período determinado y la siguiente renovación se cobra al precio completo.
- **Pay as you go** — el usuario paga un precio reducido en cada ciclo de facturación durante un período determinado y, después, la suscripción se renueva al precio completo.
- **Pay up front** — el usuario paga un precio único reducido por toda la duración de la oferta y, después, la suscripción se renueva al precio completo.
No es necesario añadir los códigos de oferta a Adapty. Apple etiqueta cada transacción durante el período de la oferta con la categoría del código de oferta. Esto incluye el canje inicial y todas las renovaciones con descuento posteriores. Adapty detecta la etiqueta y registra cada transacción con la categoría de oferta `offer_code`. Cuando termina el período de oferta y la suscripción se renueva al precio completo, la etiqueta deja de estar presente. Puedes filtrar los análisis por el tipo de oferta **Offer Code** en el [Adapty Dashboard](controls-filters-grouping-compare-proceeds).
#### Resolución de discrepancias en los ingresos \{#revenue-discrepancy-troubleshooting\}
Si observas que una transacción con código de oferta aparece en Adapty al precio completo del producto en lugar del precio reducido, verifica lo siguiente en App Store Connect:
- El código de oferta tiene los precios correctos configurados para todas las regiones donde los usuarios pueden canjearlo.
- El precio de la oferta está configurado para el país o región específica del usuario. Apple envía el precio regional en la transacción. Si no hay ningún precio regional configurado para la oferta, Apple puede enviar el precio completo del producto.
Puedes filtrar y verificar las transacciones con código de oferta en el [Adapty Dashboard](controls-filters-grouping-compare-proceeds) mediante los filtros de tipo de oferta **Offer Code** y **Offer Discount Type**.
#### Códigos promocionales heredados (obsoletos) \{#legacy-promo-codes-deprecated\}
Apple dejó obsoletos los códigos promocionales para compras in-app en marzo de 2026. Los códigos de oferta los sustituyen con más funcionalidades: elegibilidad configurable, fechas de expiración y hasta 1 millón de códigos por trimestre. Si antes usabas códigos promocionales para compras in-app, migra a los códigos de oferta en App Store Connect.
Los códigos promocionales heredados (limitados a 100 por app y versión) daban acceso gratuito a una suscripción. A diferencia de los códigos de oferta, Apple no incluía información de descuento en las transacciones con código promocional — enviaba el precio completo del producto en el recibo. Por ello, Adapty registraba estas transacciones al precio completo, lo que generaba discrepancias entre los análisis de Adapty y App Store Connect.
Si ves transacciones históricas al precio completo que deberían haber sido gratuitas, es probable que provengan de códigos promocionales heredados. Como estos códigos ya están obsoletos, migra a los códigos de oferta para un seguimiento preciso de los ingresos.
Para mostrar la pantalla de canje de códigos en tu app:
```swift showLineNumbers
Adapty.presentCodeRedemptionSheet()
```
:::danger
Según nuestras observaciones, la pantalla de canje de códigos de oferta puede no funcionar de forma fiable en algunas apps. Te recomendamos redirigir al usuario directamente al App Store.
Para ello, debes abrir una URL con el siguiente formato:
`https://apps.apple.com/redeem?ctx=offercodes&id={apple_app_id}&code={code}`
:::
---
# File: restore-purchase
---
---
title: "Restaurar compras en aplicaciones móviles con iOS SDK"
description: "Aprende cómo restaurar compras en Adapty para garantizar una experiencia de usuario fluida."
---
Restaurar compras es una función que permite a los usuarios recuperar el acceso a contenido previamente adquirido, como suscripciones o compras in-app, sin que se les cobre de nuevo. Esta función es especialmente útil para quienes hayan desinstalado y reinstalado la app, o hayan cambiado de dispositivo y quieran acceder a su contenido sin volver a pagar.
:::note
En los paywalls creados con [Paywall Builder](adapty-paywall-builder), las compras se restauran automáticamente sin que necesites añadir código adicional. Si es tu caso, puedes saltarte este paso.
:::
Para restaurar una compra cuando no usas [Paywall Builder](adapty-paywall-builder) para personalizar el paywall, llama al método `.restorePurchases()`:
```swift showLineNumbers
do {
let profile = try await Adapty.restorePurchases()
if profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive ?? false {
// successful access restore
}
} catch {
// handle the error
}
```
```swift showLineNumbers
Adapty.restorePurchases { [weak self] result in
switch result {
case let .success(profile):
if profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive ?? false {
// successful access restore
}
case let .failure(error):
// handle the error
}
}
```
Parámetros de respuesta:
| Parámetro | Descripción |
|---------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **Profile** |
Un objeto [`AdaptyProfile`](https://swift.adapty.io/documentation/adapty/adaptyprofile). Este modelo contiene información sobre niveles de acceso, suscripciones y compras únicas.
Comprueba el **estado del nivel de acceso** para determinar si el usuario tiene acceso a la app.
|
:::tip
¿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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas.
:::
---
# File: ios-transaction-management
---
---
title: "Gestión avanzada de transacciones en el SDK de iOS"
description: "Finaliza transacciones manualmente en tu app de iOS con el SDK de Adapty."
---
:::note
La gestión avanzada de transacciones está disponible en el SDK de Adapty para iOS a partir de la versión 3.12.
:::
La gestión avanzada de transacciones en Adapty te da mayor control sobre cómo se procesan, verifican y finalizan las transacciones.
Esta funcionalidad introduce tres características opcionales que trabajan en conjunto:
| Característica | Propósito |
|-------------------------------------------------------------|----------|
| [`appAccountToken`](#assign-appaccounttoken) | Vincula las transacciones de Apple con tu ID de usuario interno |
| [`jwsTransaction`](#access-the-jws-representation) | Proporciona el payload de transacción firmado por Apple para validación |
| [Finalización manual](#control-transaction-finishing-behavior) | Permite finalizar transacciones solo después de que tu backend confirme el éxito |
En conjunto, estas herramientas te permiten construir flujos de validación personalizados robustos mientras Adapty sigue sincronizando las transacciones con su backend.
:::important
La mayoría de las apps no necesitan esto.
Por defecto, Adapty valida y finaliza automáticamente las transacciones de StoreKit.
Usa esta guía solo si ejecutas tu propia validación en el backend o quieres controlar por completo el ciclo de vida de las compras.
:::
## Asignar `appAccountToken` \{#assign-appaccounttoken\}
[`appAccountToken`](https://developer.apple.com/documentation/storekit/product/purchaseoption/appaccounttoken(_:)) es un **UUID** que te permite vincular las transacciones de la App Store con la identidad interna de tus usuarios.
StoreKit asocia este token con cada transacción, de modo que tu backend puede relacionar los datos de la App Store con tus usuarios.
Usa un UUID estable generado por usuario y reutilízalo para la misma cuenta en todos los dispositivos.
Esto garantiza que las compras y las notificaciones de la App Store queden correctamente vinculadas.
Puedes establecer el token de dos formas: durante la activación del SDK o al identificar al usuario.
:::important
Siempre debes pasar `appAccountToken` junto con `customerUserId`.
Si solo pasas el token, no se incluirá en la transacción.
:::
```swift showLineNumbers
// During configuration:
let configurationBuilder =
AdaptyConfiguration
.builder(withAPIKey: "PUBLIC_SDK_KEY")
.with(customerUserId: "YOUR_USER_ID", withAppAccountToken: UUID())
do {
try await Adapty.activate(with: configurationBuilder.build())
} catch {
// handle the error
}
// Or when identifying a user:
do {
try await Adapty.identify("YOUR_USER_ID", withAppAccountToken: UUID())
} catch {
// handle the error
}
```
```swift showLineNumbers
// During configuration:
let configurationBuilder =
AdaptyConfiguration
.builder(withAPIKey: "PUBLIC_SDK_KEY")
.with(customerUserId: "YOUR_USER_ID", withAppAccountToken: )
Adapty.activate(with: configurationBuilder.build()) { error in
// handle the error
}
// Or when identifying a user:
Adapty.identify("YOUR_USER_ID", withAppAccountToken: ) { error in
if let error {
// handle the error
}
}
```
## Acceder a la representación JWS \{#access-the-jws-representation\}
Al realizar una compra, el resultado incluye la transacción de Apple en [formato JWS Compact Serialization](https://developer.apple.com/documentation/storekit/verificationresult/jwsrepresentation-21vgo).
Puedes reenviar este valor a tu backend para validación independiente o registro.
```swift
let result = try await Adapty.makePurchase(product: paywallProduct)
let jwsRepresentation = result.jwsTransaction
```
## Controlar el comportamiento de finalización de transacciones \{#control-transaction-finishing-behavior\}
Por defecto, Adapty finaliza automáticamente las transacciones de StoreKit tras la validación.
Si necesitas retrasar la finalización hasta que tu backend confirme el éxito, establece el comportamiento de finalización en manual.
En este modo:
- Adapty sigue validando las compras y sincronizándolas con su backend.
- Las transacciones permanecen sin finalizar hasta que llames explícitamente a `finish()`.
```swift
var configBuilder = AdaptyConfiguration
.builder(withAPIKey: "YOUR_API_KEY")
.with(transactionFinishBehavior: .manual)
try await Adapty.activate(with: configBuilder.build())
```
Al usar la finalización manual de transacciones, necesitas implementar el método delegado `onUnfinishedTransaction` para gestionar las transacciones sin finalizar:
```swift showLineNumbers title="Swift"
extension YourApp: AdaptyDelegate {
func onUnfinishedTransaction(_ transaction: AdaptyUnfinishedTransaction) async {
// Perform your custom validation logic here
// When ready, finish the transaction
await transaction.finish()
}
}
```
Para obtener todas las transacciones sin finalizar actuales, usa el método `getUnfinishedTransactions()`:
```swift
let unfinishedTransactions = try await Adapty.getUnfinishedTransactions()
```
---
# File: implement-observer-mode
---
---
title: "Implementar el modo Observer en el SDK de iOS"
description: "Implementa el modo Observer en Adapty para rastrear eventos de suscripción de usuarios en el SDK de iOS."
---
Si ya tienes tu propia infraestructura de compras y no estás listo para migrar completamente a Adapty, puedes explorar el [modo Observer](observer-vs-full-mode). En su forma básica, el modo Observer ofrece analíticas avanzadas e integración fluida con sistemas de atribución y analíticas.
Si esto cubre tus necesidades, solo tienes que:
1. Activarlo al configurar el SDK de Adapty estableciendo el parámetro `observerMode` en `true`.
2. [Reportar las transacciones](report-transactions-observer-mode) desde tu infraestructura de compras existente a Adapty.
Si además necesitas paywalls y pruebas A/B, se requiere una configuración adicional, como se describe a continuación.
## Configuración del modo Observer \{#observer-mode-setup\}
Activa el modo Observer si gestionas las compras y el estado de las suscripciones por tu cuenta y usas Adapty para enviar eventos de suscripción y analíticas.
:::important
Cuando se ejecuta en modo Observer, el SDK de Adapty no cerrará ninguna transacción, así que asegúrate de gestionarlo tú mismo.
:::
```swift showLineNumbers
@main
struct YourApp: App {
init() {
// Configure Adapty SDK
let configurationBuilder = AdaptyConfiguration
.builder(withAPIKey: "YOUR_PUBLIC_SDK_KEY") // Get from Adapty dashboard
.with(observerMode: true)
let config = configurationBuilder.build()
// Activate Adapty SDK asynchronously
Task {
do {
try await Adapty.activate(with: configurationBuilder)
} catch {
// Handle error appropriately for your app
print("Adapty activation failed: ", error)
}
}
var body: some Scene {
WindowGroup {
// Your content view
}
}
}
}
```
```swift showLineNumbers
Task {
do {
let configurationBuilder = AdaptyConfiguration
.builder(withAPIKey: "YOUR_PUBLIC_SDK_KEY") // Get from Adapty dashboard
.with(observerMode: true)
let config = configurationBuilder.build()
try await Adapty.activate(with: config)
} catch {
// Handle error appropriately for your app
print("Adapty activation failed: ", error)
}
}
```
Parámetros:
| Parámetro | Descripción |
| --------------------------- | ------------------------------------------------------------ |
| observerMode | Un valor booleano que controla el [modo Observer](observer-vs-full-mode). El valor predeterminado es `false`. |
## Usar los paywalls de Adapty en el modo Observer \{#using-adapty-paywalls-in-observer-mode\}
Si también quieres usar los paywalls y las funcionalidades de pruebas A/B de Adapty, puedes hacerlo, pero requiere una configuración adicional en el modo Observer. Esto es lo que necesitas hacer además de los pasos anteriores:
1. Muestra los paywalls de la forma habitual para los [paywalls con Remote Config](present-remote-config-paywalls). Para los paywalls con Paywall Builder, sigue las guías de configuración específicas para [iOS](ios-present-paywall-builder-paywalls-in-observer-mode).
3. [Asocia los paywalls](report-transactions-observer-mode) con las transacciones de compra.
---
# File: report-transactions-observer-mode
---
---
title: "Informar transacciones en Observer Mode en el SDK de iOS"
description: "Informa transacciones de compra en el Observer Mode de Adapty para obtener información sobre usuarios y realizar un seguimiento de ingresos en el SDK de iOS."
---
En el modo Observer, el SDK de Adapty no puede rastrear por sí solo las compras realizadas a través de tu sistema de compras existente. Necesitas informar las transacciones desde tu app store. Es fundamental configurar esto **antes** de publicar tu app para evitar errores en los análisis.
Usa `reportTransaction` para informar explícitamente cada transacción y que Adapty pueda reconocerla.
:::warning
**¡No omitas el reporte de transacciones!**
Si no llamas a `reportTransaction`, Adapty no reconocerá la transacción, no aparecerá en los análisis y no se enviará a las integraciones.
:::
Si usas paywalls de Adapty, incluye el `variationId` al informar una transacción. Esto vincula la compra con el paywall que la originó, garantizando un análisis preciso del paywall.
```swift showLineNumbers
do {
// every time when calling transasction.finish()
try await Adapty.reportTransaction(transaction, withVariationId: )
} catch {
// handle the error
}
```
Parámetros:
| Parámetro | Presencia | Descripción |
| --------------- | --------- | ------------------------------------------------------------ |
| **transaction** | requerido |
Para StoreKit 1: SKPaymentTransaction.
Para StoreKit 2: Transaction.
|
| **variationId** | opcional | El ID único de la variante del paywall. Obtenlo desde la propiedad `variationId` del objeto [AdaptyPaywall](https://swift.adapty.io/documentation/adapty/adaptypaywall). |
En el modo Observer, el SDK de Adapty no puede rastrear por sí solo las compras realizadas a través de tu sistema de compras existente. Necesitas informar las transacciones desde tu app store o restaurarlas. Es fundamental configurar esto **antes** de publicar tu app para evitar errores en los análisis.
Usa `reportTransaction` para enviar los datos de la transacción a Adapty.
:::warning
**¡No omitas el reporte de transacciones!**
Si no llamas a `reportTransaction`, Adapty no reconocerá la transacción, no aparecerá en los análisis y no se enviará a las integraciones.
:::
Si usas paywalls de Adapty, incluye el `withVariationId` al informar una transacción. Esto vincula la compra con el paywall que la originó, garantizando un análisis preciso del paywall.
```swift showLineNumbers
do {
// every time when calling transasction.finish()
try await Adapty.reportTransaction(transaction, withVariationId: )
} catch {
// handle the error
}
```
Parámetros:
| Parámetro | Presencia | Descripción |
| --------------- | --------- | ------------------------------------------------------------ |
| **transaction** | requerido |
Para StoreKit 1: SKPaymentTransaction.
Para StoreKit 2: Transaction.
|
| **variationId** | opcional | El ID único de la variante del paywall. Obtenlo desde la propiedad `variationId` del objeto [AdaptyPaywall](https://swift.adapty.io/documentation/adapty/adaptypaywall). |
**Reporte de transacciones**
- Las versiones hasta la 3.1.x escuchan automáticamente las transacciones en el App Store, por lo que no se requiere reporte manual.
- La versión 3.2 no es compatible con el Observer Mode.
**Asociar paywalls a transacciones**
El SDK de Adapty no puede determinar el origen de las compras, ya que eres tú quien las procesa. Por ello, si planeas usar paywalls y/o pruebas A/B en el modo Observer, debes asociar la transacción proveniente de tu app store con el paywall correspondiente en el código de tu app. Es importante hacerlo correctamente antes de publicar tu app; de lo contrario, generará errores en los análisis.
```swift
let variationId = paywall.variationId
// There are two overloads: for StoreKit 1 and StoreKit 2
Adapty.setVariationId(variationId, forPurchasedTransaction: transactionId) { error in
if error == nil {
// successful binding
}
}
```
Parámetros de la solicitud:
| Parámetro | Presencia | Descripción |
| ------------- | --------- | ------------------------------------------------------------ |
| variationId | requerido | El identificador de cadena de la variante. Puedes obtenerlo usando la propiedad `variationId` del objeto [AdaptyPaywall](https://swift.adapty.io/documentation/adapty/adaptypaywall). |
| transactionId | requerido |
Para StoreKit 1: un objeto [SKPaymentTransaction](https://developer.apple.com/documentation/storekit/skpaymenttransaction).
Para StoreKit 2: objeto [Transaction](https://developer.apple.com/documentation/storekit/transaction).
|
---
# File: ios-present-paywall-builder-paywalls-in-observer-mode
---
---
title: "Presentar paywalls del Paywall Builder en modo Observer en el SDK de iOS"
description: "Aprende a presentar paywalls de PB en modo observer para obtener mejores insights."
---
Si has personalizado un paywall con el Paywall Builder, no tienes que preocuparte por renderizarlo en el código de tu app para mostrárselo al usuario. Este tipo de paywall incluye tanto lo que se muestra como la forma en que se muestra.
:::warning
Esta sección se refiere únicamente al [modo Observer](observer-vs-full-mode). Si no trabajas en el modo Observer, consulta [iOS - Mostrar paywalls con Paywall Builder](ios-present-paywalls).
:::
Antes de comenzar a mostrar paywalls (haz clic para expandir)
1. Configura la integración inicial de Adapty [con Google Play](initial-android) y [con App Store](initial_ios).
2. Instala y configura el SDK. Asegúrate de establecer el parámetro `observerMode` en `true`. Consulta las instrucciones específicas para cada framework: [iOS](sdk-installation-ios#activate-adapty-module-of-adapty-sdk).
3. [Crea productos](create-product) en el Adapty Dashboard.
4. [Configura los paywalls, asígnales productos](create-paywall) y personalízalos con Paywall Builder en el Adapty Dashboard.
5. [Crea placements y asígnales tus paywalls](create-placement) en el Adapty Dashboard.
6. [Obtén los paywalls de Paywall Builder y su configuración](get-pb-paywalls) en el código de tu aplicación.
1. Implementa el objeto `AdaptyObserverModeResolver`:
```swift showLineNumbers title="Swift"
func observerMode(didInitiatePurchase product: AdaptyPaywallProduct,
onStartPurchase: @escaping () -> Void,
onFinishPurchase: @escaping () -> Void) {
// use the product object to handle the purchase
// use the onStartPurchase and onFinishPurchase callbacks to notify AdaptyUI about the process of the purchase
}
El evento `observerMode(didInitiatePurchase:onStartPurchase:onFinishPurchase:)` te informará de que el usuario ha iniciado una compra. Puedes activar tu flujo de compra personalizado en respuesta a este callback.
El evento `observerModeDidInitiateRestorePurchases(onStartRestore:onFinishRestore:)` te informará de que el usuario ha iniciado una restauración. Puedes activar tu flujo de restauración personalizado en respuesta a este callback.
Además, recuerda invocar los siguientes callbacks para notificar a AdaptyUI sobre el proceso de compra o restauración. Esto es necesario para que el paywall funcione correctamente, por ejemplo, para mostrar el loader, entre otras cosas:
| Callback | Descripción |
| :----------------- | :--------------------------------------------------------------------------------------------------- |
| onStartPurchase() | Este callback debe invocarse para notificar a AdaptyUI que la compra ha comenzado. |
| onFinishPurchase() | Este callback debe invocarse para notificar a AdaptyUI que la compra ha finalizado. |
| onStartRestore() | Este callback debe invocarse para notificar a AdaptyUI que la restauración ha comenzado. |
| onFinishRestore() | Este callback debe invocarse para notificar a AdaptyUI que la restauración ha finalizado. |
2. Crea un objeto de configuración de paywall:
```swift showLineNumbers title="Swift"
do {
let paywallConfiguration = try AdaptyUI.getPaywallConfiguration(
forPaywall: ,
observerModeResolver:
)
} catch {
// handle the error
}
```
Parámetros de la solicitud:
| Parámetro | Presencia | Descripción |
| :----------------------- | :------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Paywall** | obligatorio | Un objeto `AdaptyPaywall` para obtener un controlador para el paywall deseado. |
| **ObserverModeResolver** | obligatorio | El objeto `AdaptyObserverModeResolver` que implementaste en el paso anterior. |
3. Inicializa el paywall visual que quieres mostrar usando el método `.paywallController(for:products:viewConfiguration:delegate:)`:
```swift showLineNumbers title="Swift"
import Adapty
import AdaptyUI
let visualPaywall = AdaptyUI.paywallController(
with: ,
delegate:
)
```
Parámetros de la solicitud:
| Parámetro | Presencia | Descripción |
| :----------------------- | :------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Paywall Configuration** | requerido | Un objeto `AdaptyUI.PaywallConfiguration` que contiene los detalles visuales del paywall. Usa el método `AdaptyUI.getPaywallConfiguration(forPaywall:locale:)`. Consulta el tema [Obtener paywalls del Paywall Builder y su configuración](get-pb-paywalls) para más detalles. |
| **Delegate** | requerido | Un `AdaptyPaywallControllerDelegate` para escuchar los eventos del paywall. Consulta el tema [Gestión de eventos del paywall](ios-handling-events) para más detalles. |
Retorna:
| Objeto | Descripción |
| :---------------------- | :--------------------------------------------------- |
| AdaptyPaywallController | Un objeto que representa la pantalla del paywall solicitado |
Una vez creado el objeto correctamente, puedes mostrarlo así:
```swift showLineNumbers title="Swift"
present(visualPaywall, animated: true)
```
:::warning
No olvides [asociar los paywalls a las transacciones de compra](report-transactions-observer-mode). De lo contrario, Adapty no podrá determinar el paywall de origen de la compra.
:::
Para mostrar el paywall visual en la pantalla del dispositivo, usa el modificador `.paywall` en SwiftUI:
```swift showLineNumbers title="SwiftUI"
@State var paywallPresented = false
var body: some View {
Text("Hello, AdaptyUI!")
.paywall(
isPresented: $paywallPresented,
paywallConfiguration: ,
didPerformAction: { action in
switch action {
case .close:
paywallPresented = false
default:
// Handle other actions
break
}
},
didFinishRestore: { profile in /* check access level and dismiss */ },
didFailRestore: { error in /* handle the error */ },
didFailRendering: { error in paywallPresented = false }
)
}
```
The input appears to be empty or contains only a heading without any actual content to translate. Please provide the full MDX documentation you'd like translated from English to Spanish (es-ES).
| Parámetro | Presencia | Descripción |
| :----------------------- | :-------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Paywall Configuration** | obligatorio | Un objeto `AdaptyUI.PaywallConfiguration` que contiene los detalles visuales del paywall. Usa el método `AdaptyUI.getPaywallConfiguration(forPaywall:locale:)`. Consulta el tema [Obtener paywalls del Paywall Builder y su configuración](get-pb-paywalls) para más detalles. |
| **Products** | opcional | Proporciona un array de `AdaptyPaywallProducts` para optimizar el momento de visualización de los productos en pantalla. Si se pasa `nil`, AdaptyUI obtendrá automáticamente los productos necesarios. |
| **TagResolver** | opcional | Define un diccionario de etiquetas personalizadas y sus valores resueltos. Las etiquetas personalizadas actúan como marcadores de posición en el contenido del paywall y se reemplazan dinámicamente con cadenas específicas para ofrecer contenido personalizado. Consulta el tema [Etiquetas personalizadas en Paywall Builder](custom-tags-in-paywall-builder) para más detalles. |
| **ObserverModeResolver** | opcional | El objeto `AdaptyObserverModeResolver` que implementaste en el paso anterior |
Parámetros de closure:
| Parámetro de closure | Descripción |
| :------------------- | :--------------------------------------------------------------------------------------------------- |
| **didFinishRestore** | Si Adapty.restorePurchases() tiene éxito, se invocará este callback. |
| **didFailRestore** | Si Adapty.restorePurchases() falla, se invocará este callback. |
| **didFailRendering** | Si ocurre un error durante el renderizado de la interfaz, se invocará este callback. |
Consulta el tema [iOS - Manejo de eventos](ios-handling-events) para ver otros parámetros de closure.
:::warning
No olvides [asociar los paywalls a las transacciones de compra](report-transactions-observer-mode). De lo contrario, Adapty no podrá determinar el paywall de origen de la compra.
:::
Antes de empezar a mostrar paywalls (haz clic para expandir)
1. Configura la integración inicial de Adapty [con Google Play](initial-android) y [con App Store](initial_ios).
1. Instala y configura el SDK de Adapty. Asegúrate de establecer el parámetro `observerMode` en `true`. Consulta las instrucciones específicas para cada framework: [iOS](sdk-installation-ios#activate-adapty-module-of-adapty-sdk), [React Native](sdk-installation-reactnative), [Flutter](sdk-installation-flutter#activate-adapty-module-of-adapty-sdk) y [Unity](sdk-installation-unity#activate-adapty-module-of-adapty-sdk).
2. [Crea productos](create-product) en el Adapty Dashboard.
3. [Configura paywalls, asígnales productos](create-paywall) y personalízalos con Paywall Builder en el Adapty Dashboard.
4. [Crea placements y asígnales tus paywalls](create-placement) en el Adapty Dashboard.
5. [Obtén los paywalls de Paywall Builder y su configuración](get-pb-paywalls) en el código de tu app.
1. Implementa el objeto `AdaptyObserverModeDelegate`:
```swift showLineNumbers title="Swift"
func paywallController(_ controller: AdaptyPaywallController,
didInitiatePurchase product: AdaptyPaywallProduct,
onStartPurchase: @escaping () -> Void,
onFinishPurchase: @escaping () -> Void) {
// use the product object to handle the purchase
// use the onStartPurchase and onFinishPurchase callbacks to notify AdaptyUI about the process of the purchase
}
```
El evento `paywallController(_:didInitiatePurchase:onStartPurchase:onFinishPurchase:)` te informará de que el usuario ha iniciado una compra. Puedes activar tu flujo de compra personalizado en respuesta a este evento.
Además, recuerda invocar los siguientes callbacks para notificar a AdaptyUI sobre el proceso de compra. Esto es necesario para el comportamiento correcto del paywall, como mostrar el loader, entre otras cosas:
| Callback | Descripción |
| :--------------- | :-------------------------------------------------------------------------------------------------- |
| onStartPurchase | Este callback debe invocarse para notificar a AdaptyUI que la compra ha comenzado. |
| onFinishPurchase | Este callback debe invocarse para notificar a AdaptyUI que la compra ha finalizado. |
2. Inicializa el paywall visual que quieres mostrar usando el método `.paywallController(for:products:viewConfiguration:delegate:observerModeDelegate:)`:
```swift showLineNumbers title="Swift"
import Adapty
import AdaptyUI
let visualPaywall = AdaptyUI.paywallController(
for: ,
products: ,
viewConfiguration: ,
delegate:
observerModeDelegate:
)
```
Parámetros de la solicitud:
| Parámetro | Presencia | Descripción |
| :----------------------- | :-------- | :----------------------------------------------------------- |
| **Paywall** | requerido | Un objeto `AdaptyPaywall` para obtener un controlador para el paywall deseado. |
| **Products** | opcional | Proporciona un array de `AdaptyPaywallProducts` para optimizar el tiempo de visualización de los productos en pantalla. Si se pasa `nil`, AdaptyUI obtendrá automáticamente los productos necesarios. |
| **ViewConfiguration** | requerido | Un objeto `AdaptyUI.LocalizedViewConfiguration` que contiene los detalles visuales del paywall. Usa el método `AdaptyUI.getViewConfiguration(paywall:locale:)`. Consulta el tema [Obtener paywalls del Paywall Builder y su configuración](get-pb-paywalls) para más detalles. |
| **Delegate** | requerido | Un `AdaptyPaywallControllerDelegate` para escuchar los eventos del paywall. Consulta el tema [Gestión de eventos del paywall](ios-handling-events) para más detalles. |
| **ObserverModeDelegate** | requerido | El objeto `AdaptyObserverModeDelegate` que implementaste en el paso anterior. |
| **TagResolver** | opcional | Define un diccionario de etiquetas personalizadas y sus valores resueltos. Las etiquetas personalizadas actúan como marcadores de posición en el contenido del paywall y se reemplazan dinámicamente con cadenas específicas para ofrecer contenido personalizado. Consulta el tema [Etiquetas personalizadas en el Paywall Builder](custom-tags-in-paywall-builder) para más detalles. |
Devuelve:
| Objeto | Descripción |
| :---------------------- | :--------------------------------------------------- |
| AdaptyPaywallController | Un objeto que representa la pantalla del paywall solicitado |
Una vez que el objeto se haya creado correctamente, puedes mostrarlo así:
```swift showLineNumbers title="Swift"
present(visualPaywall, animated: true)
```
:::warning
No olvides [asociar los paywalls a las transacciones de compra](report-transactions-observer-mode). De lo contrario, Adapty no podrá determinar el paywall de origen de la compra.
:::
Para mostrar el paywall visual en la pantalla del dispositivo, utiliza el modificador `.paywall` en SwiftUI:
```swift showLineNumbers title="SwiftUI"
@State var paywallPresented = false
var body: some View {
Text("Hello, AdaptyUI!")
.paywall(
isPresented: $paywallPresented,
paywall: ,
configuration: ,
didPerformAction: { action in
switch action {
case .close:
paywallPresented = false
default:
// Handle other actions
break
}
},
didFinishRestore: { profile in /* check access level and dismiss */ },
didFailRestore: { error in /* handle the error */ },
didFailRendering: { error in paywallPresented = false },
observerModeDidInitiatePurchase: { product, onStartPurchase, onFinishPurchase in
// use the product object to handle the purchase
// use the onStartPurchase and onFinishPurchase callbacks to notify AdaptyUI about the process of the purchase
},
)
}
```
The input is empty — there is no MDX content to translate. Please provide the MDX documentation you'd like translated from English to Spanish (es-ES).
| Parámetro | Presencia | Descripción |
| :---------------- | :-------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Paywall** | obligatorio | Un objeto `AdaptyPaywall` para obtener un controlador del paywall deseado. |
| **Product** | opcional | Proporciona un array de `AdaptyPaywallProducts` para optimizar el momento en que se muestran los productos en pantalla. Si se pasa `nil`, AdaptyUI obtendrá automáticamente los productos necesarios. |
| **Configuration** | obligatorio | Un objeto `AdaptyUI.LocalizedViewConfiguration` que contiene los detalles visuales del paywall. Usa el método `AdaptyUI.getViewConfiguration(paywall:locale:)`. Consulta el artículo [Obtener paywalls del Paywall Builder y su configuración](get-pb-paywalls) para más detalles. |
| **TagResolver** | opcional | Define un diccionario de etiquetas personalizadas y sus valores resueltos. Las etiquetas personalizadas funcionan como marcadores de posición en el contenido del paywall y se reemplazan dinámicamente con cadenas específicas para personalizar el contenido. Consulta el artículo [Etiquetas personalizadas en el Paywall Builder](custom-tags-in-paywall-builder) para más detalles. |
The input appears to be just the text "Closure parameters:" with no actual MDX documentation content to translate. Please provide the full MDX documentation you'd like translated from English to Spanish (es-ES).
| Parámetro de closure | Descripción |
| :---------------------------------- | :------------------------------------------------------------------------------------------------- |
| **didFinishRestore** | Si Adapty.restorePurchases() tiene éxito, se invocará este callback. |
| **didFailRestore** | Si Adapty.restorePurchases() falla, se invocará este callback. |
| **didFailRendering** | Si ocurre un error durante el renderizado de la interfaz, se invocará este callback. |
| **observerModeDidInitiatePurchase** | Este callback se invoca cuando un usuario inicia una compra. |
Consulta el tema [iOS - Manejo de eventos](ios-handling-events) para conocer otros parámetros de closure.
:::warning
No olvides [asociar paywalls a las transacciones de compra](report-transactions-observer-mode). De lo contrario, Adapty no podrá determinar el paywall de origen de la compra.
:::
---
# File: ios-troubleshoot-purchases
---
---
title: "Solucionar problemas de compras en el SDK de iOS"
description: "Solucionar problemas de compras en el SDK de iOS"
---
Esta guía te ayuda a resolver los problemas más comunes al implementar compras manualmente en el SDK de iOS.
## AdaptyError.cantMakePayments en el modo observador \{#adaptyerrorcantmakepayments-in-observer-mode\}
**Problema**: Estás recibiendo `AdaptyError.cantMakePayments` al usar `makePurchase` en el modo observador.
**Causa**: En el modo observador, debes gestionar las compras por tu cuenta, no usar el método `makePurchase` de Adapty.
**Solución**: Si usas `makePurchase` para las compras, desactiva el modo observador. Puedes usar `makePurchase` o gestionar las compras por tu cuenta en el modo observador, pero no ambas opciones a la vez. Consulta [Implementar el modo observador](implement-observer-mode) para más detalles.
## No se encuentran makePurchasesCompletionHandlers \{#not-found-makepurchasescompletionhandlers\}
**Problema**: Estás experimentando problemas porque no se encuentran los `makePurchasesCompletionHandlers`.
**Causa**: Esto suele estar relacionado con problemas en las pruebas de sandbox.
**Solución**: Crea un nuevo usuario de sandbox e inténtalo de nuevo. Esto generalmente resuelve los problemas con el manejador de finalización de compras en sandbox.
## Otros problemas \{#other-issues\}
**Problema**: Estás experimentando otros problemas relacionados con las compras que no se tratan más arriba.
**Solución**: Si es necesario, migra el SDK a la versión más reciente siguiendo las [guías de migración](ios-sdk-migration-guides). Muchos problemas se resuelven en las versiones más nuevas del SDK.
---
# File: identifying-users
---
---
title: "Identificar usuarios en iOS SDK"
description: "Identifica usuarios en Adapty para mejorar las experiencias de suscripción personalizadas."
---
Adapty crea un ID de perfil interno para cada usuario. Sin embargo, si tienes tu propio sistema de autenticación, deberías establecer tu propio Customer User ID. Puedes encontrar usuarios por su Customer User ID en la sección [Perfiles](profiles-crm) y usarlo en la [API del servidor](getting-started-with-server-side-api), que se enviará a todas las integraciones.
## Establecer el ID de usuario en la configuración \{#set-customer-user-id-on-configuration\}
Si tienes un ID de usuario durante la configuración, pásalo como parámetro `customerUserId` al método `.activate()`:
```swift showLineNumbers
// In your AppDelegate class:
let configurationBuilder =
AdaptyConfiguration
.builder(withAPIKey: "PUBLIC_SDK_KEY")
.with(customerUserId: "YOUR_USER_ID")
do {
try await Adapty.activate(with: configurationBuilder.build())
} catch {
// handle the error
}
```
```swift showLineNumbers
// In your AppDelegate class:
let configurationBuilder =
AdaptyConfiguration
.builder(withAPIKey: "PUBLIC_SDK_KEY")
.with(customerUserId: "YOUR_USER_ID")
Adapty.activate(with: configurationBuilder.build()) { error in
// handle the error
}
```
:::tip
¿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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas.
:::
## Establecer el ID de usuario después de la configuración \{#set-customer-user-id-after-configuration\}
Si no tienes un ID de usuario en la configuración del SDK, puedes establecerlo más adelante en cualquier momento con el método `.identify()`. Los casos más habituales para usar este método son después del registro o la autenticación, cuando el usuario pasa de ser un usuario anónimo a un usuario autenticado.
```swift showLineNumbers
do {
try await Adapty.identify("YOUR_USER_ID")
} catch {
// handle the error
}
```
```swift showLineNumbers
Adapty.identify("YOUR_USER_ID") { error in
if let error {
// handle the error
}
}
```
Parámetros de la solicitud:
- **Customer User ID** (obligatorio): un identificador de usuario en formato string.
:::warning
Reenvío de datos significativos del usuario
En algunos casos, como cuando un usuario vuelve a iniciar sesión en su cuenta, los servidores de Adapty ya tienen información sobre ese usuario. En estos escenarios, el SDK de Adapty cambiará automáticamente para trabajar con el nuevo usuario. Si pasaste algún dato al usuario anónimo, como atributos personalizados o atribuciones de redes de terceros, debes volver a enviar esos datos para el usuario identificado.
También es importante tener en cuenta que debes volver a solicitar todos los paywalls y productos después de identificar al usuario, ya que los datos del nuevo usuario pueden ser diferentes.
:::
## Cerrar sesión e iniciar sesión \{#logging-out-and-logging-in\}
Puedes cerrar la sesión del usuario en cualquier momento llamando al método `.logout()`:
```swift showLineNumbers
do {
try await Adapty.logout()
} catch {
// handle the error
}
```
```swift showLineNumbers
Adapty.logout { error in
if error == nil {
// successful logout
}
}
```
Después puedes iniciar sesión con el usuario usando el método `.identify()`.
## Configurar appAccountToken \{#set-appaccounttoken\}
El [`appAccountToken`](https://developer.apple.com/documentation/storekit/product/purchaseoption/appaccounttoken(_:)) es un UUID que ayuda a StoreKit 2 de Apple a identificar a los usuarios entre instalaciones de la app y dispositivos.
A partir del SDK de Adapty para iOS 3.10.2, puedes pasar el `appAccountToken` al configurar el SDK o al identificar a un usuario:
```swift showLineNumbers
// During configuration:
let configurationBuilder =
AdaptyConfiguration
.builder(withAPIKey: "PUBLIC_SDK_KEY")
.with(customerUserId: "YOUR_USER_ID", withAppAccountToken: UUID())
do {
try await Adapty.activate(with: configurationBuilder.build())
} catch {
// handle the error
}
// Or when identifying a user:
do {
try await Adapty.identify("YOUR_USER_ID", withAppAccountToken: UUID())
} catch {
// handle the error
}
```
```swift showLineNumbers
// Durante la configuración:
let configurationBuilder =
AdaptyConfiguration
.builder(withAPIKey: "PUBLIC_SDK_KEY")
.with(customerUserId: "YOUR_USER_ID", withAppAccountToken: UUID())
Adapty.activate(with: configurationBuilder.build()) { error in
// maneja el error
}
// O al identificar a un usuario:
Adapty.identify("YOUR_USER_ID", withAppAccountToken: UUID()) { error in
if let error {
// maneja el error
}
}
```
A continuación, puedes iniciar sesión del usuario con el método `.identify()`.
## Detectar usuarios en varios dispositivos \{#detect-users-across-devices\}
---
no_index: true
---
Cuando el SDK se activa, lee automáticamente los derechos existentes del usuario desde StoreKit (iOS) o Google Play Billing (Android) y los sincroniza con el backend de Adapty. Una suscripción activa aparece en el perfil de Adapty sin que la app llame a `restorePurchases`.
Lo que **no** ocurre automáticamente es reconocer que un perfil en un dispositivo nuevo pertenece al mismo usuario que el perfil en el dispositivo original. Adapty relaciona perfiles por Customer User ID, así que la continuidad de identidad depende de lo que uses como CUID.
**Lo que Adapty puede detectar entre dispositivos**
| Tu configuración | Lo que Adapty detecta | Lo que debes hacer |
| --- | --- | --- |
| Customer User ID = `device_id` (sin login en la app) | El nuevo dispositivo obtiene un CUID diferente y, por tanto, un perfil diferente. La suscripción se sincroniza con el nuevo perfil mediante un evento **Access level updated**, pero `subscription_started` no se dispara — el nuevo perfil se trata como heredero de la compra original. Los análisis basados en `subscription_started` contarán de menos a los usuarios que vuelven. | Usa un ID de cuenta estable como Customer User ID para que un usuario que regresa coincida con el perfil existente en todos los dispositivos. |
| Customer User ID = ID de cuenta estable (login en cada dispositivo) | El SDK sincroniza automáticamente la suscripción en `activate()`, e `identify()` relaciona el perfil existente por CUID. | No se necesita configuración adicional — tanto la identidad como la suscripción se resuelven automáticamente. |
| Heredero de Apple Family Sharing | El miembro de la familia recibe la suscripción solo a través de un evento **Access level updated** — `subscription_started` no se dispara. | Escucha el evento **Access level updated**. Consulta [Apple Family Sharing](apple-family-sharing) para ver la matriz de eventos completa. |
| Misma cuenta de Apple/Google, distintos usuarios dentro de la app | El primer perfil que registra la compra se convierte en el principal. Los perfiles posteriores ven la suscripción a través de una cadena de herederos, con un evento **Access level updated**. | Exige login y elige un [modo de compartición](sharing-paid-access-between-user-accounts) que se adapte a tu modelo. |
**Restaurar compras en un dispositivo nuevo**
Muestra un botón "Restaurar compras" iniciado por el usuario en tu paywall. Apple App Review (directriz 3.1.1) lo exige, y actúa como alternativa cuando la sincronización automática no cubre algún caso límite. El botón debe llamar a `restorePurchases` en tu SDK.
No es necesario llamar a `restorePurchases` de forma programática al primer inicio para el uso normal — el SDK ya ejecuta el equivalente en `activate()`. Reserva las llamadas programáticas para forzar una verificación de recibo actualizada, por ejemplo al depurar un acceso que falta después de que `activate()` haya completado.
---
# File: setting-user-attributes
---
---
title: "Definir atributos de usuario en el SDK de iOS"
description: "Aprende a definir atributos de usuario en Adapty para mejorar la segmentación de audiencias."
---
Puedes añadir atributos opcionales como el email, número de teléfono, etc., al usuario de tu app. Luego puedes usar esos atributos para crear [segmentos](segments) de usuarios o simplemente consultarlos en el CRM.
### Definir atributos de usuario \{#setting-user-attributes\}
Para definir atributos de usuario, llama al método `.updateProfile()`:
```swift showLineNumbers
let builder = AdaptyProfileParameters.Builder()
.with(email: "email@email.com")
.with(phoneNumber: "+18888888888")
.with(firstName: "John")
.with(lastName: "Appleseed")
.with(gender: .other)
.with(birthday: Date())
do {
try await Adapty.updateProfile(params: builder.build())
} catch {
// handle the error
}
```
```swift showLineNumbers
let builder = AdaptyProfileParameters.Builder()
.with(email: "email@email.com")
.with(phoneNumber: "+18888888888")
.with(firstName: "John")
.with(lastName: "Appleseed")
.with(gender: .other)
.with(birthday: Date())
Adapty.updateProfile(params: builder.build()) { error in
if error != nil {
// handle the error
}
}
```
Ten en cuenta que los atributos que hayas definido previamente con el método `updateProfile` no se restablecerán.
:::tip
¿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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas.
:::
### Lista de claves permitidas \{#the-allowed-keys-list\}
Las claves `` permitidas de `AdaptyProfileParameters.Builder` y sus valores `` correspondientes son:
| Clave | Valor |
|---|-----|
|
email
phoneNumber
firstName
lastName
| String |
| gender | Enum, los valores permitidos son: `female`, `male`, `other` |
| birthday | Date |
### Atributos de usuario personalizados \{#custom-user-attributes\}
Puedes definir tus propios atributos personalizados, que normalmente están relacionados con el uso de tu app. Por ejemplo, en apps de fitness pueden ser el número de entrenamientos por semana; en apps de aprendizaje de idiomas, el nivel de conocimiento del usuario, etc. Puedes usarlos en segmentos para crear paywalls y ofertas dirigidas, y también en analíticas para identificar qué métricas de producto influyen más en los ingresos.
```swift showLineNumbers
do {
builder = try builder.with(customAttribute: "value1", forKey: "key1")
} catch {
// handle key/value validation error
}
```
Para eliminar una clave existente, usa el método `.withRemoved(customAttributeForKey:)`:
```swift showLineNumbers
do {
builder = try builder.withRemoved(customAttributeForKey: "key2")
} catch {
// handle error
}
```
A veces necesitas saber qué atributos personalizados ya están definidos. Para ello, usa el campo `customAttributes` del objeto `AdaptyProfile`.
:::warning
Ten en cuenta que el valor de `customAttributes` puede estar desactualizado, ya que los atributos de usuario pueden enviarse desde distintos dispositivos en cualquier momento, por lo que los atributos en el servidor podrían haber cambiado desde la última sincronización.
:::
### Límites \{#limits\}
- Hasta 30 atributos personalizados por usuario.
- Los nombres de clave tienen hasta 30 caracteres. El nombre de la clave puede incluir caracteres alfanuméricos y cualquiera de los siguientes: `_` `-` `.`
- El valor puede ser una cadena de texto o un número decimal con un máximo de 50 caracteres.
---
# File: subscription-status
---
---
title: "Comprobar el estado de suscripción en iOS SDK"
description: "Rastrea y gestiona el estado de suscripción de los usuarios en Adapty para mejorar la retención de clientes."
---
Con Adapty, hacer seguimiento del estado de suscripción es muy sencillo. No tienes que insertar manualmente los IDs de producto en tu código. En su lugar, puedes confirmar fácilmente el estado de suscripción de un usuario comprobando si tiene un [nivel de acceso](access-level) activo.
Antes de empezar a comprobar el estado de suscripción, configura las [Notificaciones del servidor de App Store](enable-app-store-server-notifications).
## Nivel de acceso y el objeto AdaptyProfile \{#access-level-and-the-adaptyprofile-object\}
Los niveles de acceso son propiedades del objeto [AdaptyProfile](https://swift.adapty.io/documentation/adapty/adaptyprofile). Te recomendamos recuperar el perfil cuando tu app arranque, por ejemplo al [identificar a un usuario](identifying-users#set-customer-user-id-on-configuration), y actualizarlo cada vez que se produzcan cambios. Así podrás usar el objeto de perfil sin tener que solicitarlo repetidamente.
Para recibir notificaciones de las actualizaciones del perfil, escucha los cambios tal como se describe en la sección [Escuchar actualizaciones del estado de suscripción](subscription-status#listening-for-subscription-status-updates) más abajo.
:::tip
¿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](sample-apps), que muestran la configuración completa, incluyendo la visualización de paywalls, la realización de compras y otras funcionalidades básicas.
:::
## Obtener el nivel de acceso desde el servidor \{#retrieving-the-access-level-from-the-server\}
Para obtener el nivel de acceso desde el servidor, usa el método `.getProfile()`:
```swift showLineNumbers
do {
let profile = try await Adapty.getProfile()
if profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive ?? false {
// grant access to premium features
}
} catch {
// handle the error
}
```
```swift showLineNumbers
Adapty.getProfile { result in
if let profile = try? result.get() {
// check the access
profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive ?? false {
// grant access to premium features
}
}
}
```
Parámetros de respuesta:
| Parámetro | Descripción |
| --------- |------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Profile |
Un objeto [AdaptyProfile](https://swift.adapty.io/documentation/adapty/adaptyprofile). En general, solo necesitas comprobar el estado del nivel de acceso del perfil para determinar si el usuario tiene acceso premium a la app.
El método `.getProfile` devuelve el resultado más actualizado, ya que siempre intenta consultar la API. Si por algún motivo (por ejemplo, sin conexión a internet) el SDK de Adapty no puede obtener información del servidor, se devolverán los datos de la caché. También es importante destacar que el SDK de Adapty actualiza la caché de `AdaptyProfile` periódicamente para mantener esta información lo más actualizada posible.
|
El método `.getProfile()` te proporciona el perfil del usuario a partir del cual puedes obtener el estado del nivel de acceso. Puedes tener múltiples niveles de acceso por app. Por ejemplo, si tienes una app de noticias y vendes suscripciones a diferentes temáticas de forma independiente, puedes crear los niveles de acceso "sports" y "science". Pero la mayoría de las veces solo necesitarás un nivel de acceso; en ese caso, puedes usar simplemente el nivel de acceso predeterminado "premium".
A continuación tienes un ejemplo para comprobar el nivel de acceso predeterminado "premium":
```swift showLineNumbers
do {
let profile = try await Adapty.getProfile()
let isPremium = profile.accessLevels["premium"]?.isActive ?? false
// grant access to premium features
} catch {
// handle the error
}
```
```swift showLineNumbers
Adapty.getProfile { result in
if let profile = try? result.get(),
profile.accessLevels["premium"]?.isActive ?? false {
// grant access to premium features
}
}
```
### Escuchar actualizaciones del estado de suscripción \{#listening-for-subscription-status-updates\}
Cada vez que cambia la suscripción del usuario, Adapty lanza un evento.
Para recibir mensajes de Adapty, necesitas realizar una configuración adicional:
```swift showLineNumbers
Adapty.delegate = self
// To receive subscription updates, extend `AdaptyDelegate` with this method:
nonisolated func didLoadLatestProfile(_ profile: AdaptyProfile) {
// handle any changes to subscription state
}
```
Adapty también lanza un evento al inicio de la aplicación. En ese caso, se pasará el estado de suscripción almacenado en caché.
### Caché del estado de suscripción \{#subscription-status-cache\}
La caché implementada en el SDK de Adapty almacena el estado de suscripción del perfil. Esto significa que, aunque el servidor no esté disponible, se puede acceder a los datos almacenados en caché para obtener información sobre el estado de suscripción del perfil.
No obstante, hay que tener en cuenta que no es posible solicitar datos directamente desde la caché. El SDK consulta periódicamente el servidor cada minuto para comprobar si hay actualizaciones o cambios relacionados con el perfil. Si hay alguna modificación, como nuevas transacciones u otras actualizaciones, se enviarán a los datos almacenados en caché para mantenerlos sincronizados con el servidor.
---
# File: ios-deal-with-att
---
---
title: "Gestión de ATT en el SDK de iOS"
description: "Comienza a usar Adapty en iOS para simplificar la configuración y gestión de suscripciones."
---
Si tu aplicación utiliza el framework AppTrackingTransparency y presenta una solicitud de autorización de seguimiento al usuario, debes enviar el [estado de autorización](https://developer.apple.com/documentation/apptrackingtransparency/attrackingmanager/authorizationstatus/) a Adapty.
```swift showLineNumbers
let builder = AdaptyProfileParameters.Builder()
.with(appTrackingTransparencyStatus: .authorized)
do {
try await Adapty.updateProfile(params: builder.build())
} catch {
// handle the error
}
```
```swift showLineNumbers
if #available(iOS 14, macOS 11.0, *) {
let builder = AdaptyProfileParameters.Builder()
.with(appTrackingTransparencyStatus: .authorized)
Adapty.updateProfile(params: builder.build()) { [weak self] error in
if error != nil {
// handle the error
}
}
}
```
:::warning
Recomendamos encarecidamente que envíes este valor lo antes posible cuando cambie; solo así los datos se enviarán a tiempo a las integraciones que hayas configurado.
:::
---
# File: kids-mode
---
---
title: "Modo Kids en iOS SDK"
description: "Activa el Modo Kids fácilmente para cumplir con las políticas de Apple. No se recopila IDFA ni datos publicitarios en iOS SDK."
---
Si tu aplicación iOS está destinada a niños, debes seguir las políticas de [Apple](https://developer.apple.com/kids/). Si usas el SDK de Adapty, unos pocos pasos sencillos te ayudarán a configurarlo para cumplir con estas políticas y superar las revisiones de la app store.
## ¿Qué se necesita? \{#whats-required\}
Debes configurar el SDK de Adapty para deshabilitar la recopilación de:
- [IDFA (Identificador para anunciantes)](https://en.wikipedia.org/wiki/Identifier_for_Advertisers)
- [Dirección IP](https://www.ftc.gov/system/files/ftc_gov/pdf/p235402_coppa_application.pdf)
Además, te recomendamos usar el ID de usuario del cliente con cuidado. Un ID de usuario con el formato `` se considerará claramente como recopilación de datos personales, al igual que el uso de un correo electrónico. En el Modo Kids, la mejor práctica es utilizar identificadores aleatorios o anonimizados (por ejemplo, IDs con hash o UUIDs generados por el dispositivo) para garantizar el cumplimiento.
## Activar el Modo Kids \{#enabling-kids-mode\}
### Cambios en el Adapty Dashboard \{#updates-in-the-adapty-dashboard\}
En el Adapty Dashboard, debes deshabilitar la recopilación de direcciones IP. Para ello, ve a [App settings](https://app.adapty.io/settings/general) y haz clic en **Disable IP address collection** bajo **Collect users' IP address**.
### Cambios en el código de tu aplicación \{#updates-in-your-mobile-app-code\}
Para cumplir con las políticas, deshabilita la recopilación del IDFA y la dirección IP del usuario.
Si usas Swift Package Manager, puedes activar el Modo Kids seleccionando el módulo **Adapty_KidsMode** en Xcode al instalar el SDK.
En Xcode, ve a **File** -> **Add Package Dependency...**. Ten en cuenta que los pasos para agregar dependencias de paquetes pueden variar entre versiones de Xcode, así que consulta la documentación de Xcode si es necesario.
1. Introduce la URL del repositorio:
```
https://github.com/adaptyteam/AdaptySDK-iOS.git
```
2. Selecciona la versión (se recomienda la última versión estable) y haz clic en **Add Package**.
3. En la ventana **Choose Package Products**, selecciona los módulos que necesites:
- **Adapty_KidsMode** (módulo principal)
- **AdaptyUI** (opcional: solo si planeas usar Paywall Builder)
No necesitarás ningún otro paquete.
4. Haz clic en **Add Package** para completar la instalación.
1. Actualiza tu Podfile:
- Si **no** tienes una sección `post_install`, añade el bloque de código completo a continuación.
- Si **sí** tienes una sección `post_install`, incorpora las líneas resaltadas en ella.
```ruby showLineNumbers title="Podfile"
post_install do |installer|
installer.pods_project.targets.each do |target|
// highlight-start
if target.name == 'Adapty'
target.build_configurations.each do |config|
config.build_settings['OTHER_SWIFT_FLAGS'] ||= ['$(inherited)']
config.build_settings['OTHER_SWIFT_FLAGS'] << '-DADAPTY_KIDS_MODE'
end
end
// highlight-end
end
end
```
2. Ejecuta el siguiente comando para aplicar los cambios:
```sh showLineNumbers title="Shell"
pod install
```
---
# File: get-onboardings
---
---
title: "Obtener onboardings y su configuración"
description: "Aprende cómo recuperar onboardings en Adapty."
---
Después de [diseñar la parte visual de tu onboarding](design-onboarding) con el builder en el Adapty Dashboard, puedes mostrarlo en tu app móvil. El primer paso es obtener el onboarding asociado al placement y su configuración de vista, tal como se describe a continuación.
Antes de empezar, asegúrate de que:
1. Has instalado el [SDK de Adapty para iOS, Android, React Native o Flutter](installation-of-adapty-sdks) en la versión 3.8.0 o superior.
2. Has [creado un onboarding](create-onboarding).
3. Has añadido el onboarding a un [placement](placements).
## Obtener un onboarding \{#fetch-onboarding\}
Cuando creas un [onboarding](onboardings) con nuestro builder sin código, se almacena como un contenedor con la configuración que tu app necesita obtener y mostrar. Este contenedor gestiona toda la experiencia: qué contenido aparece, cómo se presenta y cómo se procesan las interacciones del usuario (como respuestas a cuestionarios o entradas de formularios). El contenedor también registra automáticamente los eventos de analíticas, por lo que no necesitas implementar un seguimiento de vistas por separado.
Para un mejor rendimiento, obtén la configuración del onboarding con antelación para que las imágenes tengan tiempo suficiente de descargarse antes de mostrárselas a los usuarios.
Para obtener un onboarding, usa el método `getOnboarding`:
```swift showLineNumbers
do {
let onboarding = try await Adapty.getOnboarding(placementId: "YOUR_PLACEMENT_ID")
// the requested onboarding
} catch {
// handle the error
}
```
Parámetros:
| Parámetro | Presencia | Descripción |
|---------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **placementId** | obligatorio | El identificador del [Placement](placements) deseado. Es el valor que especificaste al crear un placement en el Adapty Dashboard. |
| **locale** |
opcional
por defecto: `en`
|
El identificador de la localización del onboarding. Se espera que este parámetro sea un código de idioma compuesto por una o dos subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta es para el idioma y la segunda para la región.
Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.
Consulta [Localizaciones y códigos de idioma](localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos usarlos.
|
| **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` |
Por defecto, el SDK intentará cargar los datos desde el servidor y devolverá los datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que tus usuarios siempre reciban los datos más actualizados.
Sin embargo, si crees que tus usuarios tienen una conexión a internet inestable, considera usar `.returnCacheDataElseLoad` para devolver los datos en caché si existen. En este caso, puede que los usuarios no obtengan los datos más recientes, pero experimentarán tiempos de carga más rápidos sin importar la calidad de su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar solicitudes de red.
Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra cuando se reinstala la app o mediante una limpieza manual.
El SDK de Adapty almacena los onboardings localmente en dos capas: la caché de actualización regular descrita arriba y los onboardings de respaldo. También usamos CDN para obtener los onboardings más rápido y un servidor de respaldo independiente en caso de que el CDN no sea accesible. Este sistema está diseñado para garantizar que siempre obtengas la versión más reciente de tus onboardings, asegurando la fiabilidad incluso cuando la conexión a internet es escasa.
|
| **loadTimeout** | por defecto: 5 seg |
Este valor limita el tiempo de espera para este método. Si se alcanza el límite, se devolverán los datos en caché o el respaldo local.
Ten en cuenta que, en casos excepcionales, este método puede superar ligeramente el tiempo especificado en `loadTimeout`, ya que la operación puede implicar distintas solicitudes internamente.
|
Parámetros de respuesta:
| Parámetro | Descripción |
|:----------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------|
| Onboarding | Un objeto [`AdaptyOnboarding`](https://swift.adapty.io/documentation/adapty/adaptyonboarding) con: el identificador y la configuración del onboarding, Remote Config y varias otras propiedades. |
## Acelerar la obtención del onboarding con el onboarding de audiencia predeterminada \{#speed-up-onboarding-fetching-with-default-audience-onboarding\}
Normalmente, los onboardings se obtienen casi de inmediato, por lo que no necesitas preocuparte por acelerar este proceso. Sin embargo, en casos en los que tienes muchas audiencias y onboardings, y tus usuarios tienen una conexión a internet débil, la obtención de un onboarding puede tardar más de lo deseado. En esas situaciones, puede que quieras mostrar un onboarding predeterminado para garantizar una experiencia fluida en lugar de no mostrar ninguno.
Para abordar esto, puedes usar el método `getOnboardingForDefaultAudience`, que obtiene el onboarding del placement especificado para la audiencia **All Users**. Sin embargo, es fundamental entender que el enfoque recomendado es obtener el onboarding con el método `getOnboarding`, como se detalla en la sección [Obtener un onboarding](#fetch-onboarding) anterior.
:::warning
Considera usar `getOnboarding` en lugar de `getOnboardingForDefaultAudience`, ya que este último tiene limitaciones importantes:
- **Problemas de compatibilidad**: Puede generar problemas al soportar múltiples versiones de la app, lo que requiere diseños compatibles con versiones anteriores o aceptar que las versiones más antiguas puedan mostrarse incorrectamente.
- **Sin personalización**: Solo muestra contenido para la audiencia "All Users", eliminando la segmentación basada en país, atribución o atributos personalizados.
Si la velocidad de obtención supera estos inconvenientes para tu caso de uso, utiliza `getOnboardingForDefaultAudience` como se muestra a continuación. De lo contrario, usa `getOnboarding` como se describe [arriba](#fetch-onboarding).
:::
```swift showLineNumbers
Adapty.getOnboardingForDefaultAudience(placementId: "YOUR_PLACEMENT_ID") { result in
switch result {
case let .success(onboarding):
// the requested onboarding
case let .failure(error):
// handle the error
}
}
```
Parámetros:
| Parámetro | Presencia | Descripción |
|---------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **placementId** | obligatorio | El identificador del [Placement](placements) deseado. Es el valor que especificaste al crear un placement en el Adapty Dashboard. |
| **locale** |
opcional
por defecto: `en`
|
El identificador de la localización del onboarding. Se espera que este parámetro sea un código de idioma compuesto por una o dos subetiquetas separadas por el carácter menos (**-**). La primera subetiqueta es para el idioma y la segunda para la región.
Ejemplo: `en` significa inglés, `pt-br` representa el portugués de Brasil.
Consulta [Localizaciones y códigos de idioma](localizations-and-locale-codes) para más información sobre los códigos de idioma y cómo recomendamos usarlos.
|
| **fetchPolicy** | por defecto: `.reloadRevalidatingCacheData` |
Por defecto, el SDK intentará cargar los datos desde el servidor y devolverá los datos en caché en caso de fallo. Recomendamos esta opción porque garantiza que tus usuarios siempre reciban los datos más actualizados.
Sin embargo, si crees que tus usuarios tienen una conexión a internet inestable, considera usar `.returnCacheDataElseLoad` para devolver los datos en caché si existen. En este caso, puede que los usuarios no obtengan los datos más recientes, pero experimentarán tiempos de carga más rápidos sin importar la calidad de su conexión. La caché se actualiza regularmente, por lo que es seguro usarla durante la sesión para evitar solicitudes de red.
Ten en cuenta que la caché permanece intacta al reiniciar la app y solo se borra cuando se reinstala la app o mediante una limpieza manual.
El SDK de Adapty almacena los onboardings localmente en dos capas: la caché de actualización regular descrita arriba y los onboardings de respaldo. También usamos CDN para obtener los onboardings más rápido y un servidor de respaldo independiente en caso de que el CDN no sea accesible. Este sistema está diseñado para garantizar que siempre obtengas la versión más reciente de tus onboardings, asegurando la fiabilidad incluso cuando la conexión a internet es escasa.
|
---
# File: ios-present-onboardings
---
---
title: "Mostrar onboardings en el SDK de iOS"
description: "Descubre cómo mostrar onboardings en iOS para aumentar las conversiones y los ingresos."
---
Si has personalizado un onboarding con el builder, no necesitas preocuparte por renderizarlo en el código de tu app para mostrárselo al usuario. Ese onboarding ya incluye tanto lo que debe mostrarse como la forma en que debe hacerlo.
Antes de empezar, asegúrate de que:
1. Tienes instalado el [SDK de Adapty para iOS](sdk-installation-ios) 3.8.0 o posterior.
2. Has [creado un onboarding](create-onboarding).
3. Has añadido el onboarding a un [placement](placements).
## Mostrar onboardings en Swift \{#present-onboardings-in-swift\}
Para mostrar el onboarding visual en la pantalla del dispositivo, sigue estos pasos:
1. Obtén la configuración de la vista del onboarding con el método `.getOnboardingConfiguration`.
2. Inicializa el onboarding visual que quieres mostrar usando el método `.onboardingController`:
Parámetros de la solicitud:
| Parámetro | Presencia | Descripción |
|:-------------------------------|:-----------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **onboarding configuration** | obligatorio | Un objeto `AdaptyUI.OnboardingConfiguration` con todas las propiedades del onboarding. Usa el método `AdaptyUI.getOnboardingConfiguration` para obtenerlo. |
| **delegate** | obligatorio | Un `AdaptyOnboardingControllerDelegate` para escuchar los eventos del onboarding. |
Devuelve:
| Objeto | Descripción |
|:-------------------------------|:---------------------------------------------------------------|
| **AdaptyOnboardingController** | Un objeto que representa la pantalla del onboarding solicitado |
3. Una vez creado el objeto correctamente, puedes mostrarlo en la pantalla del dispositivo:
```swift showLineNumbers title="Swift"
import Adapty
import AdaptyUI
// 0. Get an onboarding if you haven't done it yet
let onboarding = try await Adapty.getOnboarding(placementId: "YOUR_PLACEMENT_ID")
// 1. Obtain the onboarding view configuration:
let configuration = try AdaptyUI.getOnboardingConfiguration(forOnboarding: onboarding)
// 2. Create Onboarding View Controller
let onboardingController = try AdaptyUI.onboardingController(
with: configuration,
delegate:
)
// 3. Present it to the user
present(onboardingController, animated: true)
```
## Mostrar onboardings en SwiftUI \{#present-onboardings-in-swiftui\}
Para mostrar el onboarding visual en la pantalla del dispositivo con SwiftUI:
```swift showLineNumbers title="SwiftUI"
// 1. Obtain the onboarding view configuration:
let configuration = try AdaptyUI.getOnboardingConfiguration(forOnboarding: onboarding)
// 2. Display the Onboarding View within your view hierarchy
AdaptyOnboardingView(
configuration: configuration,
placeholder: { Text("Your Placeholder View") },
onCloseAction: { action in
// hide the onboarding view
},
onError: { error in
// handle the error
}
)
```
## Añadir transiciones suaves entre la pantalla de carga y el onboarding \{#add-smooth-transitions-between-the-splash-screen-and-onboarding\}
Por defecto, entre la pantalla de carga y el onboarding verás una pantalla de carga hasta que el onboarding esté completamente listo. Sin embargo, si quieres que la transición sea más fluida, puedes personalizarla y prolongar la pantalla de carga o mostrar otra cosa en su lugar.
Para hacerlo, define un placeholder (qué se mostrará mientras se carga el onboarding). Si defines un placeholder, el onboarding se cargará en segundo plano y se mostrará automáticamente cuando esté listo.
```swift showLineNumbers
extension YourOnboardingManagerClass: AdaptyOnboardingControllerDelegate {
func onboardingsControllerLoadingPlaceholder(
_ controller: AdaptyOnboardingController
) -> UIView? {
// instantiate and return the UIView which will be presented while onboarding is being loaded
}
}
```
```swift showLineNumbers
AdaptyOnboardingView(
configuration: configuration,
placeholder: {
// define your placeholder view, which will be presented while onboarding is being loaded
},
// the rest of the implementation
)
```
## Personalizar cómo se abren los enlaces en los onboardings \{#customize-how-links-open-in-onboardings\}
:::important
La personalización de cómo se abren los enlaces en los onboardings está disponible a partir de la versión 3.15.1 del SDK de Adapty.
:::
Por defecto, los enlaces de los onboardings se abren en un navegador dentro de la app. Esto ofrece una experiencia de usuario fluida al mostrar las páginas web dentro de tu aplicación, sin necesidad de cambiar de app.
Si prefieres abrir los enlaces en un navegador externo, puedes personalizar este comportamiento estableciendo el parámetro `externalUrlsPresentation` en `.externalBrowser`:
```swift showLineNumbers
let configuration = try AdaptyUI.getOnboardingConfiguration(
forOnboarding: onboarding,
externalUrlsPresentation: .externalBrowser // default – .inAppBrowser
)
```
---
# File: ios-handling-onboarding-events
---
---
title: "Gestionar eventos de onboarding en el SDK de iOS"
description: "Gestiona eventos relacionados con el onboarding en iOS usando Adapty."
---
Antes de empezar, asegúrate de que:
1. Tienes instalado el [SDK de Adapty para iOS](sdk-installation-ios) 3.8.0 o posterior.
2. Has [creado un onboarding](create-onboarding).
3. Has añadido el onboarding a un [placement](placements).
Los onboardings configurados con el builder generan eventos a los que tu app puede responder. Aprende cómo responder a estos eventos a continuación.
Para controlar o monitorizar los procesos que ocurren en la pantalla de onboarding dentro de tu app, implementa los métodos de `AdaptyOnboardingControllerDelegate`.
## Acciones personalizadas \{#custom-actions\}
En el builder, puedes añadir una acción **personalizada** a un botón y asignarle un ID.
Luego puedes usar este ID en tu código y gestionarlo como una acción personalizada. Por ejemplo, si un usuario pulsa un botón personalizado como **Login** o **Allow notifications**, se activará el método delegado `onboardingController` con el caso `.custom(id:)` y el parámetro `actionId` corresponde al **Action ID** del builder. Puedes crear tus propios IDs, como "allowNotifications".
```swift showLineNumbers
func onboardingController(_ controller: AdaptyOnboardingController, onCustomAction action: AdaptyOnboardingsCustomAction) {
if action.actionId == "allowNotifications" {
// Request notification permissions
}
}
func onboardingController(_ controller: AdaptyOnboardingController, didFailWithError error: AdaptyUIError) {
// Handle errors
}
```
Ejemplo de evento (haz clic para expandir)
```json
{
"actionId": "allowNotifications",
"meta": {
"onboardingId": "onboarding_123",
"screenClientId": "profile_screen",
"screenIndex": 0,
"screensTotal": 3
}
}
```
## Cerrar el onboarding \{#closing-onboarding\}
El onboarding se considera cerrado cuando el usuario pulsa un botón con la acción **Close** asignada.
:::important
Ten en cuenta que debes gestionar lo que ocurre cuando el usuario cierra el onboarding. Por ejemplo, debes dejar de mostrar el onboarding.
:::
Por ejemplo:
```swift showLineNumbers
func onboardingController(_ controller: AdaptyOnboardingController, onCloseAction action: AdaptyOnboardingsCloseAction) {
controller.dismiss(animated: true)
}
```
Ejemplo de evento (haz clic para expandir)
```json
{
"action_id": "close_button",
"meta": {
"onboarding_id": "onboarding_123",
"screen_cid": "final_screen",
"screen_index": 3,
"total_screens": 4
}
}
```
## Abrir un paywall \{#opening-a-paywall\}
:::tip
Gestiona este evento para abrir un paywall si quieres abrirlo dentro del onboarding. Si quieres abrir un paywall después de cerrarlo, hay una forma más directa: gestiona [`AdaptyOnboardingsCloseAction`](#closing-onboarding) y abre el paywall sin depender de los datos del evento.
:::
Si un usuario pulsa un botón que abre un paywall, recibirás el ID de acción del botón que [configuraste manualmente](get-paid-in-onboardings). La forma más fluida de trabajar con paywalls en onboardings es hacer que el ID de acción sea igual al ID del placement del paywall. Así, tras el `AdaptyOnboardingsOpenPaywallAction`, puedes usar el ID del placement para obtener y abrir el paywall directamente.
Ten en cuenta que solo se puede mostrar una vista (paywall u onboarding) en pantalla al mismo tiempo. Si presentas un paywall encima de un onboarding, no puedes controlar el onboarding en segundo plano de forma programática. Intentar descartar el onboarding cerrará el paywall en su lugar, dejando el onboarding visible. Para evitar esto, descarta siempre la vista del onboarding antes de presentar el paywall.
```swift showLineNumbers
func onboardingController(_ controller: AdaptyOnboardingController, onPaywallAction action: AdaptyOnboardingsOpenPaywallAction) {
// Dismiss onboarding before presenting paywall
controller.dismiss(animated: true) {
Task {
do {
// Get the paywall using the placement ID from the action
let paywall = try await Adapty.getPaywall(placementId: action.actionId)
// Get the paywall configuration
let paywallConfig = try await AdaptyUI.getPaywallConfiguration(
forPaywall: paywall
)
// Create and present the paywall controller
let paywallController = try AdaptyUI.paywallController(
with: paywallConfig,
delegate: self
)
// Present the paywall from the root view controller
if let rootVC = UIApplication.shared.windows.first?.rootViewController {
rootVC.present(paywallController, animated: true)
}
} catch {
// Handle any errors that occur during paywall loading
print("Failed to present paywall: \(error)")
}
}
}
}
```
Ejemplo de evento (haz clic para expandir)
```json
{
"action_id": "premium_offer_1",
"meta": {
"onboarding_id": "onboarding_123",
"screen_cid": "pricing_screen",
"screen_index": 2,
"total_screens": 4
}
}
```
## Finalización de carga del onboarding \{#finishing-loading-onboarding\}
Cuando el onboarding termina de cargarse, se invocará este método:
```swift showLineNumbers
func onboardingController(_ controller: AdaptyOnboardingController, didFinishLoading action: OnboardingsDidFinishLoadingAction) {
// Handle loading completion
}
```
Ejemplo de evento (haz clic para expandir)
```json
{
"meta": {
"onboarding_id": "onboarding_123",
"screen_cid": "welcome_screen",
"screen_index": 0,
"total_screens": 4
}
}
```
## Seguimiento de navegación \{#tracking-navigation\}
El método `onAnalyticsEvent` se llama cuando ocurren varios eventos de analíticas durante el flujo del onboarding.
El objeto `event` puede ser uno de los siguientes tipos:
| Tipo | Descripción |
|------------|-------------|
| `onboardingStarted` | Cuando el onboarding se ha cargado |
| `screenPresented` | Cuando se muestra cualquier pantalla |
| `screenCompleted` | Cuando se completa una pantalla. Incluye el `elementId` opcional (identificador del elemento completado) y la `reply` opcional (respuesta del usuario). Se activa cuando los usuarios realizan cualquier acción para salir de la pantalla. |
| `secondScreenPresented` | Cuando se muestra la segunda pantalla |
| `userEmailCollected` | Se activa cuando se recoge el email del usuario a través del campo de entrada |
| `onboardingCompleted` | Se activa cuando el usuario llega a una pantalla con el ID `final`. Si necesitas este evento, [asigna el ID `final` a la última pantalla](design-onboarding). |
| `unknown` | Para cualquier tipo de evento no reconocido. Incluye `name` (el nombre del evento desconocido) y `meta` (metadatos adicionales) |
Cada evento incluye información `meta` con los siguientes campos:
| Campo | Descripción |
|------------|-------------|
| `onboardingId` | Identificador único del flujo de onboarding |
| `screenClientId` | Identificador de la pantalla actual |
| `screenIndex` | Posición de la pantalla actual en el flujo |
| `screensTotal` | Número total de pantallas en el flujo |
Aquí tienes un ejemplo de cómo puedes usar los eventos de analíticas para el seguimiento:
```swift
func onboardingController(_ controller: AdaptyOnboardingController, onAnalyticsEvent event: AdaptyOnboardingsAnalyticsEvent) {
switch event {
case .onboardingStarted(let meta):
// Track onboarding start
trackEvent("onboarding_started", meta: meta)
case .screenPresented(let meta):
// Track screen presentation
trackEvent("screen_presented", meta: meta)
case .screenCompleted(let meta, let elementId, let reply):
// Track screen completion with user response
trackEvent("screen_completed", meta: meta, elementId: elementId, reply: reply)
case .onboardingCompleted(let meta):
// Track successful onboarding completion
trackEvent("onboarding_completed", meta: meta)
case .unknown(let meta, let name):
// Handle unknown events
trackEvent(name, meta: meta)
// Handle other cases as needed
}
}
```
Ejemplos de eventos (haz clic para expandir)
```javascript
// onboardingStarted
{
"name": "onboarding_started",
"meta": {
"onboarding_id": "onboarding_123",
"screen_cid": "welcome_screen",
"screen_index": 0,
"total_screens": 4
}
}
// screenPresented
{
"name": "screen_presented",
"meta": {
"onboarding_id": "onboarding_123",
"screen_cid": "interests_screen",
"screen_index": 2,
"total_screens": 4
}
}
// screenCompleted
{
"name": "screen_completed",
"meta": {
"onboarding_id": "onboarding_123",
"screen_cid": "profile_screen",
"screen_index": 1,
"total_screens": 4
},
"params": {
"element_id": "profile_form",
"reply": "success"
}
}
// secondScreenPresented
{
"name": "second_screen_presented",
"meta": {
"onboarding_id": "onboarding_123",
"screen_cid": "profile_screen",
"screen_index": 1,
"total_screens": 4
}
}
// userEmailCollected
{
"name": "user_email_collected",
"meta": {
"onboarding_id": "onboarding_123",
"screen_cid": "profile_screen",
"screen_index": 1,
"total_screens": 4
}
}
// onboardingCompleted
{
"name": "onboarding_completed",
"meta": {
"onboarding_id": "onboarding_123",
"screen_cid": "final_screen",
"screen_index": 3,
"total_screens": 4
}
}
```
---
# File: ios-onboarding-input
---
---
title: "Procesar datos de onboardings en iOS SDK"
description: "Guarda y utiliza datos de onboardings en tu app iOS con Adapty SDK."
---
Cuando tus usuarios responden a una pregunta de un cuestionario o introducen datos en un campo de texto, se invocará el método `onStateUpdatedAction`. Puedes guardar o procesar el tipo de campo en tu código.
Por ejemplo:
```swift showLineNumbers
func onboardingController(_ controller: AdaptyOnboardingController, onStateUpdatedAction action: AdaptyOnboardingsStateUpdatedAction) {
// Store user preferences or responses
switch action.params {
case .select(let params):
// Handle single selection
case .multiSelect(let params):
// Handle multiple selections
case .input(let params):
// Handle text input
case .datePicker(let params):
// Handle date selection
}
}
```
El objeto `action` contiene:
| Parámetro | Descripción |
|----------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `elementId` | Un identificador único para el elemento de entrada. Puedes usarlo para asociar preguntas con respuestas al guardarlas. |
| `params` | El objeto de datos de entrada del usuario que contiene las propiedades de tipo y valor. |
| `params.type` | El tipo de elemento de entrada. Puede ser: • `"select"` - Selección única entre opciones • `"multiSelect"` - Selección múltiple entre opciones • `"input"` - Campo de texto • `"datePicker"` - Selección de fecha |
| `params.value` | El o los valores seleccionados o introducidos por el usuario. La estructura depende del tipo: • `select`: Objeto con `id`, `value`, `label` • `multiSelect`: Array de objetos con `id`, `value`, `label` • `input`: Objeto con `type`, `value` • `datePicker`: Objeto con `day`, `month`, `year` |
Ejemplos de datos guardados (pueden variar según tu implementación)
```javascript
// Example of a saved select action
{
"elementId": "preference_selector",
"meta": {
"onboardingId": "onboarding_123",
"screenClientId": "preferences_screen",
"screenIndex": 1,
"screensTotal": 3
},
"params": {
"type": "select",
"value": {
"id": "option_1",
"value": "premium",
"label": "Premium Plan"
}
}
}
// Example of a saved multi-select action
{
"elementId": "interests_selector",
"meta": {
"onboardingId": "onboarding_123",
"screenClientId": "interests_screen",
"screenIndex": 2,
"screensTotal": 3
},
"params": {
"type": "multiSelect",
"value": [
{
"id": "interest_1",
"value": "sports",
"label": "Sports"
},
{
"id": "interest_2",
"value": "music",
"label": "Music"
}
]
}
}
// Example of a saved input action
{
"elementId": "name_input",
"meta": {
"onboardingId": "onboarding_123",
"screenClientId": "profile_screen",
"screenIndex": 0,
"screensTotal": 3
},
"params": {
"type": "input",
"value": {
"type": "text",
"value": "John Doe"
}
}
}
// Example of a saved date picker action
{
"elementId": "birthday_picker",
"meta": {
"onboardingId": "onboarding_123",
"screenClientId": "profile_screen",
"screenIndex": 0,
"screensTotal": 3
},
"params": {
"type": "datePicker",
"value": {
"day": 15,
"month": 6,
"year": 1990
}
}
}
```
## Casos de uso \{#use-cases\}
### Enriquecer perfiles de usuario con datos \{#enrich-user-profiles-with-data\}
Si quieres vincular de inmediato los datos de entrada con el perfil del usuario y evitar pedirle la misma información dos veces, debes [actualizar el perfil del usuario](setting-user-attributes) con los datos de entrada al gestionar la acción.
Por ejemplo, pides a los usuarios que introduzcan su nombre en el campo de texto con el ID `name` y quieres establecer el valor de ese campo como nombre de pila del usuario. Además, les pides que introduzcan su email en el campo `email`. En el código de tu app, puede quedar así:
```swift showLineNumbers
func onboardingController(_ controller: AdaptyOnboardingController, onStateUpdatedAction action: AdaptyOnboardingsStateUpdatedAction) {
// Store user preferences or responses
switch action.params {
case .input(let params):
// Handle text input
let builder = AdaptyProfileParameters.Builder()
// Map elementId to appropriate profile field
switch action.elementId {
case "name":
builder.with(firstName: params.value.value)
case "email":
builder.with(email: params.value.value)
}
do {
try await Adapty.updateProfile(params: builder.build())
} catch {
// handle the error
}
}
```
### Personalizar paywalls según las respuestas \{#customize-paywalls-based-on-answers\}
Usando cuestionarios en los onboardings, también puedes personalizar los paywalls que muestras a los usuarios tras completar el onboarding.
Por ejemplo, puedes preguntarles sobre su experiencia con el deporte y mostrar diferentes CTAs y productos a distintos grupos de usuarios.
1. [Añade un cuestionario](onboarding-quizzes) en el constructor de onboarding y asigna IDs significativos a sus opciones.
2. Gestiona las respuestas del cuestionario según sus IDs y [establece atributos personalizados](setting-user-attributes) para los usuarios.
```swift showLineNumbers
func onboardingController(_ controller: AdaptyOnboardingController, onStateUpdatedAction action: AdaptyOnboardingsStateUpdatedAction) {
// Handle quiz responses and set custom attributes
switch action.params {
case .select(let params):
// Handle quiz selection
let builder = AdaptyProfileParameters.Builder()
// Map quiz responses to custom attributes
switch action.elementId {
case "experience":
// Set custom attribute 'experience' with the selected value (beginner, amateur, pro)
try? builder.with(customAttribute: params.value.value, forKey: "experience")
}
do {
try await Adapty.updateProfile(params: builder.build())
} catch {
// handle the error
}
}
}
```
3. [Crea segmentos](segments) para cada valor de atributo personalizado.
4. Crea un [placement](placements) y añade [audiencias](audience) para cada segmento que hayas creado.
5. [Muestra un paywall](ios-paywalls) para el placement en el código de tu app. Si tu onboarding tiene un botón que abre un paywall, implementa el código del paywall como [respuesta a la acción de ese botón](ios-handling-onboarding-events#opening-a-paywall).
---
# File: ios-sdk-call-order
---
---
title: "Orden de llamadas en iOS SDK"
description: "Evita pérdidas de acceso premium, atribuciones faltantes y errores intermitentes #2002 llamando a los métodos del SDK de Adapty en el orden correcto."
---
`Adapty.activate()` debe completarse antes de llamar a cualquier otro método del SDK de Adapty. Hasta que se resuelva, el SDK no tiene estado. Cualquier llamada realizada antes o en paralelo con `activate()` falla con [`#2002 notActivated`](ios-sdk-error-handling#network-errors).
Si tu app autentica usuarios y recoges el customer user ID después del lanzamiento, llama a `Adapty.identify()` en ese momento. No llames a métodos de acción del usuario hasta que `identify` se resuelva. Las llamadas que compiten con él fallan con [`#3006 profileWasChanged`](ios-sdk-error-handling#general-errors), o aterrizan en el perfil anónimo creado en la activación. Cuando esto ocurre, la atribución, los IDs de MMP como `appsflyer_id` y la propiedad de la instalación no siempre se transfieren al perfil identificado. Si tu app no autentica usuarios, omite `identify` y sigue trabajando con el perfil anónimo.
Los SDKs de MMP y analíticas (AppsFlyer, Adjust, Branch, PostHog) siguen la misma regla. Inicialízalos primero y espera sus callbacks de UID antes de llamar a `Adapty.activate`. De lo contrario, el ID del MMP cae en un perfil anónimo breve y no siempre se transfiere al identificado. Para los detalles específicos de AppsFlyer, consulta [AppsFlyer](appsflyer).
## El orden correcto \{#the-correct-order\}
Tu camino depende de dos cosas: cuándo conoces el customer user ID y si usas un SDK de MMP o analíticas.
- **Pasos 2 y 5**: Obligatorios para cada app. Activa el SDK y luego llama a los métodos del SDK.
- **Pasos 1 y 3**: Necesarios solo si integras un SDK de MMP o analíticas (AppsFlyer, Adjust, Branch, PostHog).
- **Paso 4**: Necesario solo si tu app autentica usuarios y recoge el customer user ID después del lanzamiento.
Si tienes el customer user ID en el lanzamiento de la app, pásalo directamente a `activate()` (paso 2a). Esta ruta nunca crea un perfil anónimo, por lo que el paso 4 no es necesario.
| Paso | Llamada | Cuándo | Notas |
|------|---------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------|
| 1 | Inicializa tu SDK de MMP o analíticas (AppsFlyer, Adjust, PostHog, Branch) | Lanzamiento de la app, primero | Espera el callback de UID del MMP, por ejemplo `getAppsFlyerUID`. |
| 2a | `Adapty.activate(with: config)` con `customerUserId` establecido en la config | Lanzamiento de la app, después del paso 1, si tienes el customer user ID | Recomendado. Nunca se crea un perfil anónimo. |
| 2b | `Adapty.activate(with: config)` sin `customerUserId` | Lanzamiento de la app, después del paso 1, si no tienes el customer user ID (o nunca lo recoges) | Adapty crea un perfil anónimo. |
| 3 | `Adapty.setIntegrationIdentifier(key:value:)` para cada MMP | Después del paso 2, antes de cualquier llamada de acción del usuario | Necesario para que los IDs de MMP caigan en el perfil correcto. |
| 4 | `try await Adapty.identify("YOUR_USER_ID")` | Después del paso 3 (o paso 2 si no hay MMP), antes del paso 5 — solo en la ruta 2b con autenticación | Siempre `await`. Las llamadas concurrentes durante `identify` producen `#3006 profileWasChanged`. |
| 5 | `getPaywall`, `getPaywallProducts`, `restorePurchases`, `makePurchase`, `updateAttribution`, `updateProfile` | Después del paso 4 si llamas a `identify`; de lo contrario, después del paso 3 (o paso 2 si no hay MMP) | Estas llamadas necesitan un perfil estable. |
:::important
Saltarse estos pasos provoca pérdida de acceso premium para usuarios que regresan, `appsflyer_id` faltante en los perfiles, y paywalls devueltos contra la audiencia equivocada.
:::
## Instalaciones web2app y de embudo web \{#web2app-and-web-funnel-installs\}
Si los usuarios compran en un checkout web (Stripe, Paddle, FunnelFox) e instalan después la app nativa, el primer `activate()` del dispositivo crea un nuevo perfil anónimo. Este perfil no está vinculado al perfil web. Si puedes resolver el customer user ID antes del lanzamiento de la app (desde tu flujo de autenticación o referrer de instalación), pásalo directamente a `activate()`. De lo contrario, la compra web será invisible en el dispositivo hasta que llames a `identify("YOUR_USER_ID")` y luego a `restorePurchases`.
Para los metadatos que debes enviar con cada checkout web, consulta:
- [Stripe](stripe)
- [Paddle](paddle)
---
# File: ios-optimize-paywall-fetching
---
---
title: "Optimizar la carga del paywall en el SDK de iOS"
description: "Carga paywalls de Adapty de forma fiable: temporización, caché y patrones de respaldo para iOS."
---
Una carga fiable del paywall en iOS hace tres cosas: renderiza rápido, devuelve el paywall orientado a la audiencia y recurre al respaldo sin problemas cuando la red es lenta. Las reglas siguientes cubren los patrones de temporización, caché y respaldo para conseguirlo.
:::tip
Las reglas asumen que `Adapty.activate()` y `Adapty.identify()` ya han resuelto. Consulta [Orden de llamadas en el SDK de iOS](ios-sdk-call-order).
:::
## Reglas y errores comunes \{#rules-and-pitfalls\}
| Haz esto | No hagas esto | Por qué |
|---|---|---|
| Carga el placement que vas a mostrar. | Precarga todos los placements de forma concurrente al iniciar. | La precarga masiva bloquea el hilo principal y produce una pantalla en negro durante la ráfaga. |
| Llama a `getPaywall` después de que la atribución haya tenido tiempo de resolverse — por ejemplo, 1–2 segundos después de `activate` o cuando se dispare `onProfileUpdate`. | Llama a `getPaywall` en `App.init()`. | La atribución aún no ha llegado. El paywall se resuelve contra la audiencia predeterminada y omite silenciosamente los segmentos y la personalización de ASA. |
| Establece un `loadTimeout` y configura un [paywall de respaldo](fallback-paywalls) para cada placement. | Esperes a `getPaywall` indefinidamente. | Sin un tiempo límite, los usuarios con mala conectividad ven una pantalla en blanco hasta que la red responde — o cierran la app. |
Consulta [Cargar paywalls y productos](fetch-paywalls-and-products) para la referencia de los parámetros `fetchPolicy` y `loadTimeout`, y [Placements](placements) para elegir el placement adecuado.
## Ajustar para conectividad deficiente \{#tune-for-poor-connectivity\}
Para mercados con conectividad consistentemente deficiente (zonas rurales, transporte, regiones afectadas por el enrutamiento):
- Establece `fetchPolicy: .returnCacheDataElseLoad` en cada carga excepto la primera.
- Configura un [paywall de respaldo](fallback-paywalls) para cada placement en el Adapty Dashboard.
- Establece `loadTimeout` en 3–5 segundos y acepta el respaldo cuando se agote el tiempo.
- No condicionar la visualización del paywall a `getProfile()`. Llama a `getPaywall` de forma independiente para que un perfil lento no bloquee la UI.
---
# File: ios-test
---
---
title: "Prueba y lanzamiento en el SDK de iOS"
description: "Aprende a comprobar el estado de la suscripción en tu app de iOS con Adapty."
---
Si ya has implementado el SDK de Adapty en tu app de iOS, querrás comprobar que todo está configurado correctamente y que las compras funcionan como se espera. Esto implica probar tanto la integración del SDK como las compras en sí.
## Prueba tu app \{#test-your-app\}
Para realizar pruebas exhaustivas de tus compras in-app, incluyendo pruebas en sandbox y validación en TestFlight, consulta nuestra [guía de pruebas](test-purchases-in-sandbox).
## Prepárate para el lanzamiento \{#prepare-for-release\}
Antes de enviar tu app al store, sigue el [checklist de lanzamiento](release-checklist) para confirmar que:
- La conexión con el store y las notificaciones del servidor están configuradas
- Las compras se completan y se reportan a Adapty
- El acceso se desbloquea y se restaura correctamente
- Se cumplen los requisitos de privacidad y revisión
---
# File: InvalidProductIdentifiers
---
---
title: "Solución al error Code-1000 noProductIDsFound"
description: "Resuelve los errores de identificador de producto no válido al gestionar suscripciones en Adapty."
---
El error con código 1000, `noProductIDsFound`, indica que ninguno de los productos que solicitaste en el paywall está disponible para su compra en el App Store, aunque aparezcan listados allí. Este error puede ir acompañado a veces de una advertencia `InvalidProductIdentifiers`. Si la advertencia aparece sin error, puedes ignorarla sin problema.
Si te encuentras con el error `noProductIDsFound`, sigue estos pasos para resolverlo:
## Paso 1. Verifica el bundle ID \{#step-2-check-bundle-id\}
---
no_index: true
---
1. Abre [App Store Connect](https://appstoreconnect.apple.com/apps). Selecciona tu app y ve a la sección **General** → **App Information**.
2. Copia el **Bundle ID** en la subsección **General Information**.
3. Abre la pestaña [**App settings** -> **iOS SDK**](https://app.adapty.io/settings/ios-sdk) desde el menú superior de Adapty y pega el valor copiado en el campo **Bundle ID**.
4. Vuelve a la página **App information** en App Store Connect y copia el **Apple ID** desde allí.
5. En la página [**App settings** -> **iOS SDK**](https://app.adapty.io/settings/ios-sdk) del Adapty Dashboard, pega el ID en el campo **Apple app ID**.
## Paso 2. Verifica los productos
1. Ve a **App Store Connect** y navega a [**Monetization** → **Subscriptions**](https://appstoreconnect.apple.com/apps/6477523342/distribution/subscriptions) en el menú de la izquierda.
2. Haz clic en el nombre del grupo de suscripciones. Verás tus productos listados en la sección **Subscriptions**.
3. Asegúrate de que el producto que estás probando figure como **Ready to Submit**. Si no es así, sigue las instrucciones de la página [Producto en App Store](app-store-products).
4. Compara el ID del producto de la tabla con el que aparece en la pestaña [**Products**](https://app.adapty.io/products) del Adapty Dashboard. Si los IDs no coinciden, copia el ID del producto de la tabla y [crea un producto](create-product) con ese ID en el Adapty Dashboard.
## Paso 3. Verifica la disponibilidad del producto \{#step-4-check-product-availability\}
1. Vuelve a **App Store Connect** y abre la misma sección **Subscriptions**.
2. Haz clic en el nombre del grupo de suscripciones para ver tus productos.
3. Selecciona el producto que estás probando.
4. Desplázate hasta la sección **Availability** y comprueba que todos los países y regiones necesarios están listados.
## Paso 4. Verifica los precios del producto \{#step-5-check-product-prices\}
1. De nuevo, ve a la sección **Monetization** → **Subscriptions** en **App Store Connect**.
2. Haz clic en el nombre del grupo de suscripciones.
3. Selecciona el producto que estás probando.
4. Desplázate hacia abajo hasta **Subscription Pricing** y expande la sección **Current Pricing for New Subscribers**.
5. Asegúrate de que todos los precios necesarios están listados.
## Paso 5. Verifica que el estado de pago de la app, la cuenta bancaria y los formularios fiscales estén activos
1. En la página de inicio de [**App Store Connect**](https://appstoreconnect.apple.com/), haz clic en **Business**.
2. Selecciona el nombre de tu empresa.
3. Desplázate hacia abajo y comprueba que tu **Paid Apps Agreement**, **Bank Account** y **Tax forms** aparecen todos como **Active**.
Siguiendo estos pasos, deberías poder resolver la advertencia `InvalidProductIdentifiers` y publicar tus productos en el store.
## Paso 6. Vuelve a crear el producto si está bloqueado
Los pasos 1–5 pueden superarse correctamente — estado `Approved`, Bundle ID coincidente, API key válida — y aun así el SDK devuelve `1000 noProductIDsFound`. En ese caso, puede que el producto esté bloqueado en el registro de Apple. El registro de productos de Apple entra ocasionalmente en un estado en el que un producto existe en la interfaz de App Store Connect pero no queda expuesto en la ruta de consulta de StoreKit.
Elimina el producto en App Store Connect y vuelve a crearlo con el mismo ID de producto. Espera hasta 24 horas tras la recreación para que se propague.
---
# File: cantMakePayments
---
---
title: "Solución para el error Code-1003 cantMakePayment"
description: "Resuelve el error de realización de pagos al gestionar suscripciones en Adapty."
---
El error 1003, `cantMakePayments`, indica que no es posible realizar compras in-app en este dispositivo.
Si encuentras el error `cantMakePayments`, normalmente se debe a una de estas razones:
- Restricciones del dispositivo: El error no está relacionado con Adapty. Consulta las soluciones más abajo.
- Configuración del modo Observer: El método `makePurchase` y el modo Observer no pueden usarse al mismo tiempo. Consulta la sección más abajo.
## Problema: Restricciones del dispositivo \{#issue-device-restrictions\}
| Problema | Solución |
|-------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------|
| Restricciones de Screen Time | Desactiva las restricciones de compras in-app en [Screen Time](https://support.apple.com/en-us/102470) |
| Cuenta suspendida | Contacta con el soporte de Apple para resolver problemas con la cuenta |
| Restricciones regionales | Usa una cuenta de App Store de una región compatible |
## Problema: Usar el modo Observer y makePurchase a la vez \{#issue-using-both-observer-mode-and-makepurchase\}
Si usas `makePurchases` para gestionar las compras, no necesitas el modo Observer. El [modo Observer](observer-vs-full-mode) solo es necesario si implementas la lógica de compra tú mismo.
Por lo tanto, si usas `makePurchase`, puedes eliminar sin problema la activación del modo Observer del código de inicialización del SDK.
---
# File: migration-to-ios-315
---
---
title: "Migrar Adapty iOS SDK a la v. 3.15"
description: "Migra al Adapty iOS SDK v3.15 para mejorar el rendimiento y acceder a nuevas funciones de monetización."
---
Si usas [Paywall Builder](adapty-paywall-builder) en [modo Observer](observer-vs-full-mode), a partir del iOS SDK 3.15 necesitas implementar un nuevo método `observerModeDidInitiateRestorePurchases(onStartRestore:onFinishRestore:)`. Este método ofrece mayor control sobre la lógica de restauración, permitiéndote gestionar las restauraciones de compras en tu propio flujo personalizado. Para ver todos los detalles de implementación, consulta [Presentar paywalls de Paywall Builder en modo Observer](ios-present-paywall-builder-paywalls-in-observer-mode).
```diff showLineNumbers
func observerMode(didInitiatePurchase product: AdaptyPaywallProduct,
onStartPurchase: @escaping () -> Void,
onFinishPurchase: @escaping () -> Void) {
// use the product object to handle the purchase
// use the onStartPurchase and onFinishPurchase callbacks to notify AdaptyUI about the process of the purchase
}
+ func observerModeDidInitiateRestorePurchases(onStartRestore: @escaping () -> Void,
+ onFinishRestore: @escaping () -> Void) {
+ // use the onStartRestore and onFinishRestore callbacks to notify AdaptyUI about the process of the restore
+ }
```
---
# File: migration-to-ios-sdk-34
---
---
title: "Migrar el SDK de Adapty para iOS a la v. 3.4"
description: "Migra al SDK de Adapty para iOS v3.4 para obtener mejor rendimiento y nuevas funciones de monetización."
---
El SDK de Adapty 3.4.0 es una versión principal que introduce mejoras que requieren pasos de migración por tu parte.
## Actualizar la activación del SDK de Adapty \{#update-adapty-sdk-activation\}
```diff showLineNumbers
// In your AppDelegate class:
let configurationBuilder =
AdaptyConfiguration
.builder(withAPIKey: "PUBLIC_SDK_KEY")
- Adapty.activate(with: configurationBuilder) { error in
+ Adapty.activate(with: configurationBuilder.build()) { error in
// handle the error
}
```
**Actualizar los archivos de paywall de respaldo**
Actualiza tus archivos de paywall de respaldo para garantizar la compatibilidad con la nueva versión del SDK:
1. [Descarga los archivos de paywall de respaldo actualizados](fallback-paywalls) desde el Adapty Dashboard.
2. [Sustituye los paywalls de respaldo existentes en tu aplicación móvil](ios-use-fallback-paywalls) por los nuevos archivos.
```diff showLineNumbers
@main
struct SampleApp: App {
init() {
let configurationBuilder =
AdaptyConfiguration
.builder(withAPIKey: "PUBLIC_SDK_KEY")
Task {
- try await Adapty.activate(with: configurationBuilder)
+ try await Adapty.activate(with: configurationBuilder.build())
}
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
```
**Actualizar los archivos de paywall de respaldo**
Actualiza tus archivos de paywall de respaldo para garantizar la compatibilidad con la nueva versión del SDK:
1. [Descarga los archivos de paywall de respaldo actualizados](fallback-paywalls) desde el Adapty Dashboard.
2. [Sustituye los paywalls de respaldo existentes en tu aplicación móvil](ios-use-fallback-paywalls) por los nuevos archivos.
---
# File: migration-to-ios330
---
---
title: "Migrar el SDK de Adapty iOS a la v. 3.3"
description: "Migra al SDK de Adapty iOS v3.3 para mejorar el rendimiento y acceder a nuevas funciones de monetización."
---
El SDK de Adapty 3.3.0 es una versión mayor que incluye mejoras que pueden requerir algunos pasos de migración de tu parte.
1. Renombra `Adapty.Configuration` a `AdaptyConfiguration`.
2. Renombra el método `getViewConfiguration` a `getPaywallConfiguration`.
3. Elimina los parámetros `didCancelPurchase` y `paywall` de SwiftUI, y renombra el parámetro `viewConfiguration` a `paywallConfiguration`.
4. Actualiza cómo procesas las compras in-app promocionales desde el App Store eliminando el parámetro `defermentCompletion` del método `AdaptyDelegate`.
5. Elimina el método `getProductsIntroductoryOfferEligibility`.
6. Actualiza las configuraciones de integración para Adjust, AirBridge, Amplitude, AppMetrica, Appsflyer, Branch, Facebook Ads, Firebase y Google Analytics, Mixpanel, OneSignal, Pushwoosh.
7. Actualiza la implementación del modo Observer.
## Renombrar Adapty.Configuration a AdaptyConfiguration \{#rename-adaptyconfiguration-to-adaptyconfiguration\}
Actualiza el código de activación del SDK de Adapty iOS de la siguiente manera:
```diff showLineNumbers
// In your AppDelegate class:
let configurationBuilder =
- Adapty.Configuration
+ AdaptyConfiguration
.builder(withAPIKey: "PUBLIC_SDK_KEY")
.with(observerMode: false)
.with(customerUserId: "YOUR_USER_ID")
.with(idfaCollectionDisabled: false)
.with(ipAddressCollectionDisabled: false)
Adapty.activate(with: configurationBuilder) { error in
// handle the error
}
```
```diff showLineNumbers
@main
struct SampleApp: App {
init()
let configurationBuilder =
- Adapty.Configuration
+ AdaptyConfiguration
.builder(withAPIKey: "PUBLIC_SDK_KEY")
.with(observerMode: false) // optional
.with(customerUserId: "YOUR_USER_ID") // optional
.with(idfaCollectionDisabled: false) // optional
.with(ipAddressCollectionDisabled: false) // optional
Task {
try await Adapty.activate(with: configurationBuilder)
}
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
```
## Renombrar el método getViewConfiguration a getPaywallConfiguration \{#rename-getviewconfiguration-method-to-getpaywallconfiguration\}
Actualiza el nombre del método para obtener el `viewConfiguration` del paywall:
```diff showLineNumbers
guard paywall.hasViewConfiguration else {
// use your custom logic
return
}
do {
- let paywallConfiguration = try await AdaptyUI.getViewConfiguration(
+ let paywallConfiguration = try await AdaptyUI.getPaywallConfiguration(
forPaywall: paywall
)
// use loaded configuration
} catch {
// handle the error
}
```
Para más detalles sobre el método, consulta [Obtener la configuración de vista del paywall diseñado con Paywall Builder](get-pb-paywalls#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder).
## Cambiar parámetros en SwiftUI \{#change-parameters-in-swiftui\}
Se han realizado las siguientes actualizaciones en SwiftUI:
1. Se ha eliminado el parámetro `didCancelPurchase`. Usa `didFinishPurchase` en su lugar.
2. El método `.paywall()` ya no acepta un objeto paywall.
3. El parámetro `paywallConfiguration` ha reemplazado al parámetro `viewConfiguration`.
Actualiza tu código de la siguiente manera:
```diff showLineNumbers
@State var paywallPresented = false
var body: some View {
Text("Hello, AdaptyUI!")
.paywall(
isPresented: $paywallPresented,
- paywall: ,
- viewConfiguration: ,
+ paywallConfiguration: ,
didPerformAction: { action in
switch action {
case .close:
paywallPresented = false
default:
// Handle other actions
break
}
},
- didFinishPurchase: { product, profile in paywallPresented = false },
+ didFinishPurchase: { product, purchaseResult in /* handle the result*/ },
didFailPurchase: { product, error in /* handle the error */ },
didFinishRestore: { profile in /* check access level and dismiss */ },
didFailRestore: { error in /* handle the error */ },
didFailRendering: { error in paywallPresented = false }
- didCancelPurchase: { product in /* handle the result*/}
)
}
```
## Actualizar el manejo de compras in-app promocionales desde el App Store \{#update-handling-of-promotional-in-app-purchases-from-app-store\}
Actualiza cómo gestionas las compras in-app promocionales desde el App Store eliminando el parámetro `defermentCompletion` del método `AdaptyDelegate`, como se muestra en el siguiente ejemplo:
```swift showLineNumbers title="Swift"
final class YourAdaptyDelegateImplementation: AdaptyDelegate {
nonisolated func shouldAddStorePayment(for product: AdaptyDeferredProduct) -> Bool {
// 1a.
// Return `true` to continue the transaction in your app.
// 1b.
// Store the product object and return `false` to defer or cancel the transaction.
false
}
// 2. Continue the deferred purchase later on by passing the product to `makePurchase`
func continueDeferredPurchase() async {
let storedProduct: AdaptyDeferredProduct = // get the product object from the 1b.
do {
try await Adapty.makePurchase(product: storedProduct)
} catch {
// handle the error
}
}
}
```
## Eliminar el método getProductsIntroductoryOfferEligibility \{#remove-getproductsintroductoryoffereligibility-method\}
Antes del SDK de Adapty iOS 3.3.0, el objeto de producto siempre incluía las ofertas, independientemente de si el usuario era elegible. Tenías que verificar manualmente la elegibilidad antes de usar la oferta.
Ahora, el objeto de producto solo incluye una oferta si el usuario es elegible. Esto significa que ya no necesitas verificar la elegibilidad: si hay una oferta presente, el usuario es elegible.
Si aún quieres ver las ofertas para usuarios que no son elegibles, consulta `sk1Product` y `sk2Product`.
## Actualizar la configuración del SDK de integraciones de terceros \{#update-third-party-integration-sdk-configuration\}
A partir del SDK de Adapty iOS 3.3.0, hemos actualizado la API pública del método `updateAttribution`. Anteriormente aceptaba un diccionario `[AnyHashable: Any]`, lo que te permitía pasar objetos de atribución directamente desde varios servicios. Ahora requiere un `[String: any Sendable]`, por lo que deberás convertir los objetos de atribución antes de pasarlos.
Para garantizar que las integraciones funcionen correctamente con el SDK de Adapty iOS 3.3.0 y versiones posteriores, actualiza las configuraciones de tu SDK para las siguientes integraciones tal como se describe en las secciones a continuación.
### Adjust \{#adjust\}
Actualiza el código de tu aplicación móvil como se muestra a continuación. Para ver el ejemplo completo, consulta la [configuración del SDK para la integración con Adjust](adjust#connect-your-app-to-adjust).
```diff showLineNumbers
class AdjustModuleImplementation {
- func updateAdjustAttribution() {
- Adjust.attribution { attribution in
- guard let attributionDictionary = attribution?.dictionary()?.toSendableDict() else { return }
-
- Adjust.adid { adid in
- guard let adid else { return }
-
- Adapty.updateAttribution(attributionDictionary, source: .adjust, networkUserId: adid) { error in
- // handle the error
- }
- }
- }
- }
+ func updateAdjustAdid() {
+ Adjust.adid { adid in
+ guard let adid else { return }
+
+ Adapty.setIntegrationIdentifier(key: "adjust_device_id", value: adid)
+ }
+ }
+
+ func updateAdjustAttribution() {
+ Adjust.attribution { attribution in
+ guard let attribution = attribution?.dictionary() else {
+ return
+ }
+
+ Adapty.updateAttribution(attribution, source: "adjust")
+ }
+ }
}
```
```diff showLineNumbers
class YourAdjustDelegateImplementation {
// Find your implementation of AdjustDelegate
// and update adjustAttributionChanged method:
func adjustAttributionChanged(_ attribution: ADJAttribution?) {
- if let attribution = attribution?.dictionary()?.toSendableDict() {
- Adapty.updateAttribution(attribution, source: .adjust)
+ if let attribution = attribution?.dictionary() {
+ Adapty.updateAttribution(attribution, source: "adjust")
}
}
}
```
### AirBridge \{#airbridge\}
Actualiza el código de tu aplicación móvil como se muestra a continuación. Para ver el ejemplo completo, consulta la [configuración del SDK para la integración con AirBridge](airbridge#connect-your-app-to-airbridge).
```diff showLineNumbers
import AirBridge
- let builder = AdaptyProfileParameters.Builder()
- .with(airbridgeDeviceId: AirBridge.deviceUUID())
-
- Adapty.updateProfile(params: builder.build())
+ do {
+ try await Adapty.setIntegrationIdentifier(
+ key: "airbridge_device_id",
+ value: AirBridge.deviceUUID()
+ )
+ } catch {
+ // handle the error
+ }
```
### Amplitude \{#amplitude\}
Actualiza el código de tu aplicación móvil como se muestra a continuación. Para ver el ejemplo completo, consulta la [configuración del SDK para la integración con Amplitude](amplitude#sdk-configuration).
```diff showLineNumbers
import Amplitude
- let builder = AdaptyProfileParameters.Builder()
- .with(amplitudeUserId: Amplitude.instance().userId)
- .with(amplitudeDeviceId: Amplitude.instance().deviceId)
-
- Adapty.updateProfile(params: builder.build())
+ do {
+ try await Adapty.setIntegrationIdentifier(
+ key: "amplitude_user_id",
+ value: Amplitude.instance().userId
+ )
+ try await Adapty.setIntegrationIdentifier(
+ key: "amplitude_device_id",
+ value: Amplitude.instance().deviceId
+ )
+ } catch {
+ // handle the error
+ }
```
### AppMetrica \{#appmetrica\}
Actualiza el código de tu aplicación móvil como se muestra a continuación. Para ver el ejemplo completo, consulta la [configuración del SDK para la integración con AppMetrica](appmetrica#sdk-configuration).
```diff showLineNumbers
import AppMetricaCore
- if let deviceID = AppMetrica.deviceID {
- let builder = AdaptyProfileParameters.Builder()
- .with(appmetricaDeviceId: deviceID)
- .with(appmetricaProfileId: "YOUR_ADAPTY_CUSTOMER_USER_ID")
-
- Adapty.updateProfile(params: builder.build())
- }
+ if let deviceID = AppMetrica.deviceID {
+ do {
+ try await Adapty.setIntegrationIdentifier(
+ key: "appmetrica_device_id",
+ value: deviceID
+ )
+ try await Adapty.setIntegrationIdentifier(
+ key: "appmetrica_profile_id",
+ value: "YOUR_ADAPTY_CUSTOMER_USER_ID"
+ )
+ } catch {
+ // handle the error
+ }
+ }
```
### AppsFlyer \{#appsflyer\}
Actualiza el código de tu aplicación móvil como se muestra a continuación. Para ver el ejemplo completo, consulta la [configuración del SDK para la integración con AppsFlyer](appsflyer#connect-your-app-to-appsflyer).
```diff showLineNumbers
class YourAppsFlyerLibDelegateImplementation {
// Find your implementation of AppsFlyerLibDelegate
// and update onConversionDataSuccess method:
func onConversionDataSuccess(_ conversionInfo: [AnyHashable : Any]) {
let uid = AppsFlyerLib.shared().getAppsFlyerUID()
- Adapty.updateAttribution(
- conversionInfo.toSendableDict(),
- source: .appsflyer,
- networkUserId: uid
- )
+ Adapty.setIntegrationIdentifier(key: "appsflyer_id", value: uid)
+ Adapty.updateAttribution(conversionInfo, source: "appsflyer")
}
}
```
### Branch \{#branch\}
Actualiza el código de tu aplicación móvil como se muestra a continuación. Para ver el ejemplo completo, consulta la [configuración del SDK para la integración con Branch](branch#connect-your-app-to-branch).
```diff showLineNumbers
class YourBranchImplementation {
func initializeBranch() {
// Pass the attribution you receive from the initializing method of Branch iOS SDK to Adapty.
Branch.getInstance().initSession(launchOptions: launchOptions) { (data, error) in
- if let data = data?.toSendableDict() {
- Adapty.updateAttribution(data, source: .branch)
- }
+ if let data {
+ Adapty.updateAttribution(data, source: "branch")
+ }
}
}
}
```
### Facebook Ads \{#facebook-ads\}
Actualiza el código de tu aplicación móvil como se muestra a continuación. Para ver el ejemplo completo, consulta la [configuración del SDK para la integración con Facebook Ads](facebook-ads#connect-your-app-to-facebook-ads).
```diff showLineNumbers
import FacebookCore
- let builder = AdaptyProfileParameters.Builder()
- .with(facebookAnonymousId: AppEvents.shared.anonymousID)
-
- do {
- try Adapty.updateProfile(params: builder.build())
- } catch {
- // handle the error
- }
+ do {
+ try await Adapty.setIntegrationIdentifier(
+ key: "facebook_anonymous_id",
+ value: AppEvents.shared.anonymousID
+ )
+ } catch {
+ // handle the error
+ }
```
### Firebase y Google Analytics \{#firebase-and-google-analytics\}
Actualiza el código de tu aplicación móvil como se muestra a continuación. Para ver el ejemplo completo, consulta la [configuración del SDK para la integración con Firebase y Google Analytics](firebase-and-google-analytics).
```diff showLineNumbers
import FirebaseCore
import FirebaseAnalytics
FirebaseApp.configure()
- if let appInstanceId = Analytics.appInstanceID() {
- let builder = AdaptyProfileParameters.Builder()
- .with(firebaseAppInstanceId: appInstanceId)
- Adapty.updateProfile(params: builder.build()) { error in
- // handle error
- }
- }
+ if let appInstanceId = Analytics.appInstanceID() {
+ do {
+ try await Adapty.setIntegrationIdentifier(
+ key: "firebase_app_instance_id",
+ value: appInstanceId
+ )
+ } catch {
+ // handle the error
+ }
+ }
```
### Mixpanel \{#mixpanel\}
Actualiza el código de tu aplicación móvil como se muestra a continuación. Para ver el ejemplo completo, consulta la [configuración del SDK para la integración con Mixpanel](mixpanel#sdk-configuration).
```diff showLineNumbers
import Mixpanel
- let builder = AdaptyProfileParameters.Builder()
- .with(mixpanelUserId: Mixpanel.mainInstance().distinctId)
-
- do {
- try await Adapty.updateProfile(params: builder.build())
- } catch {
- // handle the error
- }
+ do {
+ try await Adapty.setIntegrationIdentifier(
+ key: "mixpanel_user_id",
+ value: Mixpanel.mainInstance().distinctId
+ )
+ } catch {
+ // handle the error
+ }
```
### OneSignal \{#onesignal\}
Actualiza el código de tu aplicación móvil como se muestra a continuación. Para ver el ejemplo completo, consulta la [configuración del SDK para la integración con OneSignal](onesignal#sdk-configuration).
```diff showLineNumbers
// PlayerID (pre-v5 OneSignal SDK)
// in your OSSubscriptionObserver implementation
func onOSSubscriptionChanged(_ stateChanges: OSSubscriptionStateChanges) {
if let playerId = stateChanges.to.userId {
- let params = AdaptyProfileParameters.Builder()
- .with(oneSignalPlayerId: playerId)
- .build()
-
- Adapty.updateProfile(params:params) { error in
- // check error
- }
+ Task {
+ try await Adapty.setIntegrationIdentifier(
+ key: "one_signal_player_id",
+ value: playerId
+ )
+ }
}
}
// SubscriptionID (v5+ OneSignal SDK)
OneSignal.Notifications.requestPermission({ accepted in
- let id = OneSignal.User.pushSubscription.id
-
- let builder = AdaptyProfileParameters.Builder()
- .with(oneSignalSubscriptionId: id)
-
- Adapty.updateProfile(params: builder.build())
+ Task {
+ try await Adapty.setIntegrationIdentifier(
+ key: "one_signal_subscription_id",
+ value: OneSignal.User.pushSubscription.id
+ )
+ }
}, fallbackToSettings: true)
```
### Pushwoosh \{#pushwoosh\}
Actualiza el código de tu aplicación móvil como se muestra a continuación. Para ver el ejemplo completo, consulta la [configuración del SDK para la integración con Pushwoosh](pushwoosh#sdk-configuration).
```diff showLineNumbers
- let params = AdaptyProfileParameters.Builder()
- .with(pushwooshHWID: Pushwoosh.sharedInstance().getHWID())
- .build()
-
- Adapty.updateProfile(params: params) { error in
- // handle the error
- }
+ do {
+ try await Adapty.setIntegrationIdentifier(
+ key: "pushwoosh_hwid",
+ value: Pushwoosh.sharedInstance().getHWID()
+ )
+ } catch {
+ // handle the error
+ }
```
## Actualizar la implementación del modo Observer \{#update-observer-mode-implementation\}
Actualiza cómo vinculas los paywalls a las transacciones. Anteriormente, usabas el método `setVariationId` para asignar el `variationId`. Ahora puedes incluir el `variationId` directamente al registrar la transacción usando el nuevo método `reportTransaction`. Consulta el ejemplo de código final en [Asociar paywalls con transacciones de compra en el modo Observer](report-transactions-observer-mode).
:::warning
Recuerda registrar la transacción usando el método `reportTransaction`. Si omites este paso, Adapty no reconocerá la transacción, no otorgará niveles de acceso, no la incluirá en los análisis ni la enviará a las integraciones. ¡Este paso es esencial!
:::
```diff showLineNumbers
- let variationId = paywall.variationId
-
- // There are two overloads: for StoreKit 1 and StoreKit 2
- Adapty.setVariationId(variationId, forPurchasedTransaction: transaction) { error in
- if error == nil {
- // successful binding
- }
- }
+ do {
+ // every time when calling transaction.finish()
+ try await Adapty.reportTransaction(transaction, withVariationId: )
+ } catch {
+ // handle the error
+ }
```
---
# File: migration-to-ios-sdk-v3
---
---
title: "Migrar el SDK de iOS de Adapty a la v. 3.0"
description: "Migra al SDK de iOS de Adapty v3.0 para mejorar el rendimiento y acceder a nuevas funciones de monetización."
---
El SDK de Adapty v.3.0 incorpora compatibilidad con el nuevo y emocionante [Adapty Paywall Builder](adapty-paywall-builder), la nueva versión de la herramienta no-code y fácil de usar para crear paywalls. Con su máxima flexibilidad y amplias capacidades de diseño, tus paywalls serán más efectivos y rentables.
:::info
Ten en cuenta que la librería AdaptyUI está obsoleta y ahora se incluye como parte del AdaptySDK.
:::
## Reinstalar el SDK de Adapty v3.x mediante Swift Package Manager \{#reinstall-adapty-sdk-v3x-via-swift-package-manager\}
1. Elimina la dependencia del paquete AdaptyUI SDK de tu proyecto, ya no la necesitarás.
2. Aunque ya lo tengas, deberás volver a añadir la dependencia del SDK de Adapty. Para ello, en Xcode, abre **File** -> **Add Package Dependency...**. Ten en cuenta que la forma de añadir dependencias de paquetes puede variar según la versión de Xcode. Consulta la documentación de Xcode si es necesario.
3. Introduce la URL del repositorio `https://github.com/adaptyteam/AdaptySDK-iOS.git`
4. Elige la versión y haz clic en el botón **Add package**.
5. Elige los módulos que necesitas:
1. **Adapty** es el módulo obligatorio.
2. **AdaptyUI** es un módulo opcional que necesitas si piensas usar el [Adapty Paywall Builder](adapty-paywall-builder).
6. Xcode añadirá la dependencia del paquete a tu proyecto y podrás importarla. Para ello, en la ventana **Choose Package Products**, haz clic de nuevo en el botón **Add package**. El paquete aparecerá en la lista **Packages**.
## Reinstalar el SDK de Adapty v3.x mediante CocoaPods \{#reinstall-adapty-sdk-v3x-via-cocoapods\}
1. Añade Adapty a tu `Podfile`. Elige los módulos que necesitas:
1. **Adapty** es el módulo obligatorio.
2. **AdaptyUI** es un módulo opcional que necesitas si piensas usar el [Adapty Paywall Builder](adapty-paywall-builder).
2. ```shell showLineNumbers title="Podfile"
pod 'Adapty', '~> 3.2.0'
pod 'AdaptyUI', '~> 3.2.0' # optional module needed only for Paywall Builder
```
3. Ejecuta:
```sh showLineNumbers title="Shell"
pod install
```
Esto crea un archivo `.xcworkspace` para tu app. Usa este archivo para todo el desarrollo futuro de tu aplicación.
Activa los módulos del SDK de Adapty y AdaptyUI. Antes de la v3.0 no se activaba AdaptyUI, así que recuerda **añadir la activación de AdaptyUI**. Los parámetros no cambian, así que déjalos tal como están.
```swift showLineNumbers
// In your AppDelegate class:
let configurationBuilder =
AdaptyConfiguration
.Builder(withAPIKey: "PUBLIC_SDK_KEY")
.with(observerMode: false)
.with(customerUserId: "YOUR_USER_ID")
.with(idfaCollectionDisabled: false)
.with(ipAddressCollectionDisabled: false)
Adapty.activate(with: configurationBuilder) { error in
// handle the error
}
// Only if you are going to use AdaptyUI
AdaptyUI.activate()
```
```swift title="" showLineNumbers
@main
struct SampleApp: App {
init()
let configurationBuilder =
AdaptyConfiguration
.Builder(withAPIKey: "PUBLIC_SDK_KEY")
.with(observerMode: false) // optional
.with(customerUserId: "YOUR_USER_ID") // optional
.with(idfaCollectionDisabled: false) // optional
.with(ipAddressCollectionDisabled: false) // optional
Adapty.activate(with: configurationBuilder) { error in
// handle the error
}
// Only if you are going to use AdaptyUI
AdaptyUI.activate()
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
```
---
# End of Documentation
_Generated on: 2026-05-22T06:52:52.294Z_
_Successfully processed: 41/41 files_