Migrar el SDK de Adapty iOS a v4.0
El SDK de Adapty iOS 4.0 (beta) introduce flows y renombra las APIs de paywall en consecuencia. Las nuevas APIs funcionan tanto con el nuevo Flow Builder como con el Paywall Builder existente — no se requieren cambios de configuración en el Adapty Dashboard.
Referencia rápida
| v3 | v4 |
|---|---|
Adapty.getPaywall(placementId:locale:) | Adapty.getFlow(placementId:) |
AdaptyUI.getPaywallConfiguration(forPaywall:) | AdaptyUI.getFlowConfiguration(forFlow:locale:) |
Adapty.getPaywallProducts(paywall:) | Adapty.getPaywallProducts(flow:) |
Adapty.logShowPaywall(_:) | Adapty.logShowFlow(_:) |
AdaptyPaywallController | AdaptyFlowController |
AdaptyPaywallControllerDelegate | AdaptyFlowControllerDelegate |
AdaptyUI.paywallController(with:delegate:) | AdaptyUI.flowController(with:delegate:) |
.paywall() (modificador SwiftUI) | .flow() |
AdaptyPaywallView | AdaptyFlowView |
didFailRenderingWith: / didFailRendering: | didReceiveError: |
didFinishPurchase (opcional, se cierra automáticamente al completarse) | didFinishPurchase (obligatorio, sin cierre automático) |
Productos del paquete Adapty_KidsMode / AdaptyUI_KidsMode | Trait de paquete KidsMode |
Adapty.updateAttribution(_:source:) (source: String) | Adapty.updateAttribution(_:source:) (source: AdaptyAttributionSource) |
Adapty.setIntegrationIdentifier(key:value:) | Adapty.setIntegrationIdentifier(_:) (AdaptyIntegrationIdentifier) |
Versión mínima de iOS
Adapty iOS SDK 4.0 eleva el objetivo mínimo de despliegue de iOS 13.0 a iOS 15.0. Establece el iOS Deployment Target de tu proyecto en 15.0 o superior antes de actualizar.
Instalación: CocoaPods ya no está soportado
El SDK de Adapty para iOS 4.0 elimina el soporte de CocoaPods. Instala el SDK con Swift Package Manager.
Si tu proyecto todavía usa CocoaPods, elimina los pods Adapty y AdaptyUI de tu Podfile, ejecuta pod install para limpiarlos y luego añade el paquete en Xcode desde File → Add Package Dependency usando https://github.com/adaptyteam/AdaptySDK-iOS.git.
Modo Kids: productos separados reemplazados por un trait de paquete
En v3, el Modo Kids se habilitaba seleccionando los productos de paquete separados Adapty_KidsMode y AdaptyUI_KidsMode y renombrando tus imports. A partir de 4.0.0-beta.2, estos productos se han eliminado. El Modo Kids es ahora un trait de paquete Swift llamado KidsMode en el paquete Adapty estándar — al activarlo, se elimina en tiempo de compilación el uso de IDFA y AdSupport en todo el SDK.
Para migrar:
-
En la ventana Choose Package Products, selecciona los productos Adapty y AdaptyUI normales en lugar de Adapty_KidsMode y AdaptyUI_KidsMode.
-
Activa el trait
KidsMode. En Xcode 26.4 o posterior, actívalo para la dependencia AdaptySDK-iOS en la vista Package Dependencies de tu proyecto. Si añades Adapty como dependencia enPackage.swift(requiereswift-tools-version6.1 o posterior), actívalo allí:
.package(
url: "https://github.com/adaptyteam/AdaptySDK-iOS.git",
exact: "4.0.0-beta.2",
traits: ["KidsMode"]
)
-
Cambia tus imports de vuelta a los módulos normales:
- import Adapty_KidsMode - import AdaptyUI_KidsMode + import Adapty + import AdaptyUI
Las versiones de Xcode anteriores a la 26.4 no pueden activar traits para un proyecto de Xcode desde la interfaz. En ese caso, añade un pequeño paquete Swift local que dependa de Adapty con el trait KidsMode activado, y haz que el target de tu app dependa de ese paquete.
APIs eliminadas
Adapty.getPaywallProductsWithoutDeterminingOffer(paywall:)— eliminada. Todos los productos incluyen ahora información sobre la oferta, por lo que el paso de elegibilidad separado ya no es necesario.AdaptyPaywallProductWithoutDeterminingOffer— eliminada. Los callbacks que antes pasaban este tipo (comodidSelectProduct) ahora pasanAdaptyPaywallProduct.
Compras in-app promocionadas en App Store temporalmente eliminadas
Como parte de la migración a StoreKit 2, el SDK de Adapty para iOS 4.0 elimina el soporte para las compras in-app promocionadas en App Store. El método delegado shouldAddStorePayment(for:) y el tipo AdaptyDeferredProduct que recibe no están disponibles en la versión 4.0.
Esta eliminación es temporal: el soporte para compras in-app promocionadas volverá en una versión posterior de la rama 4.x. Si tu app depende de las compras in-app promocionadas, mantente en el SDK de iOS 3.x hasta que el soporte regrese.
Obtener paywalls
getPaywall + getPaywallConfiguration → getFlow + getFlowConfiguration
Los tipos devueltos cambian de AdaptyPaywall / AdaptyUI.PaywallConfiguration a AdaptyFlow / AdaptyUI.FlowConfiguration. El parámetro locale deja de estar en la llamada de obtención y pasa a getFlowConfiguration:
- let paywall = try await Adapty.getPaywall(placementId: "YOUR_PLACEMENT_ID", locale: "en")
- let paywallConfiguration = try await AdaptyUI.getPaywallConfiguration(forPaywall: paywall)
+ let flow = try await Adapty.getFlow(placementId: "YOUR_PLACEMENT_ID")
+ let flowConfiguration = try await AdaptyUI.getFlowConfiguration(forFlow: flow, locale: "en")
getPaywallProducts(paywall:) → getPaywallProducts(flow:)
getPaywallProducts ahora recibe un AdaptyFlow devuelto por Adapty.getFlow:
- let products = try await Adapty.getPaywallProducts(paywall: paywall)
+ let products = try await Adapty.getPaywallProducts(flow: flow)
Seguimiento de vistas de paywall
logShowPaywall(:) → logShowFlow(:)
logShowPaywall ha sido renombrado a logShowFlow y ahora recibe un AdaptyFlow en lugar de un AdaptyPaywall. El evento sigue registrándose contra la misma variación, por lo que las métricas de funnel y de prueba A/B existentes seguirán funcionando sin cambios en el dashboard.
- try await Adapty.logShowPaywall(paywall)
+ try await Adapty.logShowFlow(flow)
Al igual que en v3, no es necesario llamar a este método al mostrar flows o paywalls renderizados por el Flow Builder o el Paywall Builder — Adapty registra esas vistas automáticamente.
didFinishPurchase ahora es obligatorio
En v3, didFinishPurchase era opcional: si no lo implementabas, el paywall se cerraba automáticamente tras una compra exitosa. A partir de 4.0.0-beta.2, esta implementación de cierre automático se ha eliminado para que un flow pueda continuar después de una compra exitosa — por ejemplo, para mostrar las pantallas restantes de tu flow. Ahora tú decides qué ocurre tras una compra: cerrar la pantalla o no hacer nada para que el flow continúe.
- UIKit: los que implementen
AdaptyFlowControllerDelegatedeben implementardidFinishPurchase— ya no tiene una implementación por defecto. - SwiftUI: el closure
didFinishPurchasede.flow(...)yAdaptyFlowView(...)ahora es obligatorio (no opcional), igual quedidFailPurchaseydidFinishRestore.
Para mantener el comportamiento de v3, cierra la pantalla tú mismo:
func flowController(
_ controller: AdaptyFlowController,
didFinishPurchase product: AdaptyPaywallProduct,
purchaseResult: AdaptyPurchaseResult
) {
if !purchaseResult.isPurchaseCancelled {
controller.dismiss(animated: true)
}
}
UIKit
AdaptyPaywallController → AdaptyFlowController
Renombra el tipo de controlador y el método factory:
- let controller = try AdaptyUI.paywallController(
- with: paywallConfiguration,
- delegate: self
- )
+ let controller = try AdaptyUI.flowController(
+ with: flowConfiguration,
+ delegate: self
+ )
AdaptyPaywallControllerDelegate → AdaptyFlowControllerDelegate
Renombra el protocolo y actualiza cada firma de método. Ten en cuenta que didSelectProduct ahora recibe AdaptyPaywallProduct en lugar del eliminado AdaptyPaywallProductWithoutDeterminingOffer, y didFinishPurchase ahora debe implementarse — ya no tiene una implementación predeterminada.
- class YourClass: AdaptyPaywallControllerDelegate {
+ class YourClass: AdaptyFlowControllerDelegate {
- func paywallControllerDidAppear(_ controller: AdaptyPaywallController) { }
+ func flowControllerDidAppear(_ controller: AdaptyFlowController) { }
- func paywallControllerDidDisappear(_ controller: AdaptyPaywallController) { }
+ func flowControllerDidDisappear(_ controller: AdaptyFlowController) { }
- func paywallController(_ controller: AdaptyPaywallController,
- didPerform action: AdaptyUI.Action) { }
+ func flowController(_ controller: AdaptyFlowController,
+ didPerform action: AdaptyUI.Action) { }
- func paywallController(_ controller: AdaptyPaywallController,
- didSelectProduct product: AdaptyPaywallProductWithoutDeterminingOffer) { }
+ func flowController(_ controller: AdaptyFlowController,
+ didSelectProduct product: AdaptyPaywallProduct) { }
- func paywallController(_ controller: AdaptyPaywallController,
- didStartPurchase product: AdaptyPaywallProduct) { }
+ func flowController(_ controller: AdaptyFlowController,
+ didStartPurchase product: AdaptyPaywallProduct) { }
- func paywallController(_ controller: AdaptyPaywallController,
- didFinishPurchase product: AdaptyPaywallProduct,
- purchaseResult: AdaptyPurchaseResult) { }
+ func flowController(_ controller: AdaptyFlowController,
+ didFinishPurchase product: AdaptyPaywallProduct,
+ purchaseResult: AdaptyPurchaseResult) { }
- func paywallController(_ controller: AdaptyPaywallController,
- didFailPurchase product: AdaptyPaywallProduct,
- error: AdaptyError) { }
+ func flowController(_ controller: AdaptyFlowController,
+ didFailPurchase product: AdaptyPaywallProduct,
+ error: AdaptyError) { }
- func paywallControllerDidStartRestore(_ controller: AdaptyPaywallController) { }
+ func flowControllerDidStartRestore(_ controller: AdaptyFlowController) { }
- func paywallController(_ controller: AdaptyPaywallController,
- didFinishRestoreWith profile: AdaptyProfile) { }
+ func flowController(_ controller: AdaptyFlowController,
+ didFinishRestoreWith profile: AdaptyProfile) { }
- func paywallController(_ controller: AdaptyPaywallController,
- didFailRestoreWith error: AdaptyError) { }
+ func flowController(_ controller: AdaptyFlowController,
+ didFailRestoreWith error: AdaptyError) { }
- func paywallController(_ controller: AdaptyPaywallController,
- didFailRenderingWith error: AdaptyUIError) { }
+ func flowController(_ controller: AdaptyFlowController,
+ didReceiveError error: AdaptyUIError) { }
- func paywallController(_ controller: AdaptyPaywallController,
- didFailLoadingProductsWith error: AdaptyError) -> Bool { }
+ func flowController(_ controller: AdaptyFlowController,
+ didFailLoadingProductsWith error: AdaptyError) -> Bool { }
- func paywallController(_ controller: AdaptyPaywallController,
- didPartiallyLoadProducts failedIds: [String]) { }
+ func flowController(_ controller: AdaptyFlowController,
+ didPartiallyLoadProducts failedIds: [String]) { }
- func paywallController(_ controller: AdaptyPaywallController,
- didFinishWebPaymentNavigation product: AdaptyPaywallProduct?,
- error: AdaptyError?) { }
+ func flowController(_ controller: AdaptyFlowController,
+ didFinishWebPaymentNavigation product: AdaptyPaywallProduct?,
+ error: AdaptyError?) { }
}
SwiftUI
Modificador .paywall() → .flow()
Renombra el modificador, actualiza el nombre del parámetro de configuración y añade el closure didFinishPurchase ahora obligatorio:
@State var flowPresented = false // rename freely — the variable name is your choice
var body: some View {
Text("Hello, AdaptyUI!")
- .paywall(
+ .flow(
isPresented: $flowPresented,
- paywallConfiguration: paywallConfiguration,
+ flowConfiguration: flowConfiguration,
+ didFinishPurchase: { product, purchaseResult in /* dismiss, or do nothing to let the flow continue */ },
didFailPurchase: { product, error in /* handle the error */ },
didFinishRestore: { profile in /* check access level and dismiss */ },
didFailRestore: { error in /* handle the error */ },
- didFailRendering: { error in flowPresented = false }
+ didReceiveError: { error in flowPresented = false }
)
}
El callback renombrado se activa para los mismos errores de renderizado que didFailRendering, más los nuevos errores en tiempo de ejecución del script del flow (excepciones de JavaScript con código AdaptyUIError 4105 — .jsException). Los cuerpos de los handlers existentes no necesitan cambios en el código: solo hay que renombrar el parámetro.
AdaptyPaywallView → AdaptyFlowView
Renombra la vista, actualiza el parámetro de configuración, añade el closure didFinishPurchase ahora obligatorio, y actualiza cualquier closure didSelectProduct — ahora recibe AdaptyPaywallProduct en lugar del eliminado AdaptyPaywallProductWithoutDeterminingOffer:
- AdaptyPaywallView(
- paywallConfiguration: paywallConfiguration,
- didSelectProduct: { product: AdaptyPaywallProductWithoutDeterminingOffer in /* handle */ },
+ AdaptyFlowView(
+ flowConfiguration: flowConfiguration,
+ didSelectProduct: { product: AdaptyPaywallProduct in /* handle */ },
+ didFinishPurchase: { product, purchaseResult in /* dismiss, or do nothing to let the flow continue */ },
didFailPurchase: { product, error in /* handle the error */ },
didFinishRestore: { profile in /* check access level and dismiss */ },
didFailRestore: { error in /* handle the error */ },
- didFailRendering: { error in /* handle the error */ }
+ didReceiveError: { error in /* handle the error */ }
)
Recursos personalizados de AdaptyUI
AdaptyUICustomVideoAsset
Hay dos cambios que afectan a todos los puntos de llamada existentes:
.playerahora aceptaAVPlayeren lugar deAVQueuePlayer.- Todos los casos han ganado un parámetro adicional al final:
resolution: CGSize?. Pasanilpara mantener el comportamiento actual, o pasa el tamaño en píxeles real para que el reproductor pueda reservar espacio de layout (relación de aspecto =width / height) antes de que cargue el vídeo.
- case file(url: URL, preview: AdaptyUICustomImageAsset?)
- case remote(url: URL, preview: AdaptyUICustomImageAsset?)
- case player(item: AVPlayerItem, player: AVQueuePlayer, preview: AdaptyUICustomImageAsset?)
+ case file(url: URL, preview: AdaptyUICustomImageAsset?, resolution: CGSize?)
+ case remote(url: URL, preview: AdaptyUICustomImageAsset?, resolution: CGSize?)
+ case player(item: AVPlayerItem, player: AVPlayer, preview: AdaptyUICustomImageAsset?, resolution: CGSize?)
Identificadores de atribución e integración
updateAttribution(_:source:)
El parámetro source cambia de String al nuevo tipo AdaptyAttributionSource, y el anteriormente anidado AdaptyProfile.AttributionSource se renombra al nivel superior AdaptyAttributionSource. Usa una de las fuentes predefinidas, o pasa un literal de cadena para cualquier otra fuente: AdaptyAttributionSource conforma con ExpressibleByStringLiteral, por lo que las llamadas con literales de cadena existentes siguen compilando.
- try await Adapty.updateAttribution(attribution, source: "adjust")
+ try await Adapty.updateAttribution(attribution, source: .adjust)
Fuentes predefinidas: .appleAds, .adjust, .appsflyer, .branch, .tenjin. Si guardas la fuente en una variable String, envuélvela así: AdaptyAttributionSource(rawValue: yourSource).
setIntegrationIdentifier(_:)
setIntegrationIdentifier(key:value:) es reemplazado por un método variádico que acepta uno o más valores de tipo AdaptyIntegrationIdentifier. Usa los métodos de fábrica predefinidos en lugar de claves de cadena sin formato:
- try await Adapty.setIntegrationIdentifier(key: "appsflyer_id", value: uid)
+ try await Adapty.setIntegrationIdentifier(.appsflyerId(uid))
Puedes establecer varios identificadores en una sola llamada:
try await Adapty.setIntegrationIdentifier(
.appsflyerId(uid),
.adjustDeviceId(adid)
)
Reemplaza cada cadena de clave antigua por su método de fábrica:
| clave v3 | factory v4 |
|---|---|
"adjust_device_id" | .adjustDeviceId(_:) |
"airbridge_device_id" | .airbridgeDeviceId(_:) |
"amplitude_user_id" | .amplitudeUserId(_:) |
"amplitude_device_id" | .amplitudeDeviceId(_:) |
"appmetrica_device_id" | .appmetricaDeviceId(_:) |
"appmetrica_profile_id" | .appmetricaProfileId(_:) |
"appsflyer_id" | .appsflyerId(_:) |
"branch_id" | .branchId(_:) |
"facebook_anonymous_id" | .facebookAnonymousId(_:) |
"firebase_app_instance_id" | .firebaseAppInstanceId(_:) |
"mixpanel_user_id" | .mixpanelUserId(_:) |
"one_signal_subscription_id" | .oneSignalSubscriptionId(_:) |
"one_signal_player_id" | .oneSignalPlayerId(_:) |
"posthog_distinct_user_id" | .posthogDistinctUserId(_:) |
"pushwoosh_hwid" | .pushwooshHWID(_:) |
"tenjin_analytics_installation_id" | .tenjinAnalyticsInstallationId(_:) |