Requisitos previos
Complete los pasos en Integración de un SDK Singular: Planificación y requisitos previos antes de proceder con esta integración.
Importante: Estos pasos de prerrequisitos son necesarios para cualquier integración de Singular SDK.
Instalación
Elija el método de instalación que prefiera. Recomendamos CocoaPods para la mayoría de los proyectos.
Decisión rápida:
- ¿Ya utiliza CocoaPods? Utilice el método 1
- ¿Sólo proyecto SPM? Utilice el método 2
- ¿Sin gestor de paquetes? Utilice el método 3
Métodos de instalación
Método 1: CocoaPods (Recomendado)
Requisitos:
- CocoaPods instalado(guía de instalación)
- Acceso por terminal al directorio de tu proyecto
Pasos de instalación:
-
Inicializar Podfile (omitir si ya tienes uno):
cd /path/to/your/project pod init -
Añade Singular SDK a tu Podfile:
platform :ios, '12.0' target 'YourAppName' do use_frameworks! # Singular SDK pod 'Singular-SDK' end -
Instalar dependencias:
pod install -
Abrir espacio de trabajo: A partir de ahora, abre
.xcworkspaceen lugar de.xcodeproj - Sólo proyectos Swift: Crear cabecera puente(ver más abajo)
Método 2: Gestor de paquetes Swift
Pasos de instalación:
- En Xcode: Archivo → Añadir paquetes
-
Introduce la URL del repositorio:
https://github.com/singular-labs/Singular-iOS-SDK - Selecciona la versión y haz clic en Añadir paquete
-
Añade los frameworks necesarios:
Ir a Build Phases → Link Binary with Librariesy añadir:-
Vincular bibliotecas requeridas:
Vaya a Build Phases → Link Binary With Librariesy añada:- Libsqlite3.0.tbd
- SystemConfiguration.framework
- Security.framework
- Libz.tbd
- AdSupport.framework
- WebKit.framework
- StoreKit.framework
- AdServices.framework (marcar como Opcional)
-
Vincular bibliotecas requeridas:
- Sólo proyectos Swift: Crear cabecera puente(ver más abajo)
Método 3: Instalación manual del framework
Cuándo utilizarlo: Utilice este método sólo si no puede utilizar CocoaPods o SPM.
Descargar Framework:
- Xcode 12+: Descargar .xcframework
- Xcode 11 e inferiores: Descargar .framework
Pasos de instalación:
- Descomprimir el framework descargado
- En Xcode: Haz clic con el botón derecho del ratón en el proyecto → Añadir archivos a [Proyecto]
- Selecciona Crear grupos y añade la carpeta del framework
-
Enlaza las librerías necesarias:
Ve a Build Phases → Link Binary With Librariesy añade:- Libsqlite3.0.tbd
- SystemConfiguration.framework
- Security.framework
- Libz.tbd
- AdSupport.framework
- WebKit.framework
- StoreKit.framework
- AdServices.framework (marcar como Opcional)
-
Incrustar marco de trabajo:
Vaya a General → Frameworks, bibliotecas y contenido incrustado.
Establezca Singular framework en Incrustar y firmar
Cabecera de puente Swift
Importante: Necesario para proyectos Swift que utilicen CocoaPods o SPM.
-
Cree el archivo de cabecera:
Xcode → Archivo → Nuevo → Archivo → Archivo de cabecera.
NómbreloYourProjectName-Bridging-Header.h -
Añade la importación:
#import <Singular/Singular.h> -
Enlace en ajustes de compilación:
Build Settings → Objective-C Bridging Header
Establecer en:YourProjectName/YourProjectName-Bridging-Header.h
Configurar e inicializar SDK
Crea un objeto de configuración e inicializa el SDK en los puntos de entrada de tu app.
Crear objeto de configuración
Configuración básica
Cree un objeto SingularConfig con sus credenciales del SDK y funciones opcionales. Esta configuración es universal para todas las arquitecturas de aplicaciones.
Obtenga sus credenciales: Encuentra tu SDK Key y SDK Secret en la plataforma Singular en Herramientas de Desarrollador → Integración SDK.
// MARK: - Singular Configuration
private func getConfig() -> SingularConfig? {
// Create config with your credentials
guard let config = SingularConfig(
apiKey: "YOUR_SDK_KEY",
andSecret: "YOUR_SDK_SECRET"
) else {
return nil
}
// OPTIONAL: Wait for ATT consent (if showing ATT prompt)
// Remove this line if NOT using App Tracking Transparency
config.waitForTrackingAuthorizationWithTimeoutInterval = 300
// OPTIONAL: Support custom ESP domains for deep links
config.espDomains = ["links.your-domain.com"]
// OPTIONAL: Handle deep links
config.singularLinksHandler = { params in
if let params = params {
self.handleDeeplink(params)
}
}
return config
}
// MARK: - OPTIONAL: Deep link handler implementation
private func handleDeeplink(_ params: SingularLinkParams) {
// Guard clause: Exit if no deep link provided
guard let deeplink = params.getDeepLink() else {
return
}
// Extract deep link parameters
let passthrough = params.getPassthrough()
let isDeferred = params.isDeferred()
let urlParams = params.getUrlParameters()
#if DEBUG
// Debug logging only - stripped from production builds
print("Singular Links Handler")
print("Singular deeplink received:", deeplink)
print("Singular passthrough received:", passthrough ?? "none")
print("Singular isDeferred received:", isDeferred ? "YES" : "NO")
print("Singular URL Params received:", urlParams ?? [:])
#endif
// TODO: Navigate to appropriate screen based on deep link
// Add deep link handling code here. Navigate to appropriate screen.
}
#pragma mark - Singular Configuration
- (SingularConfig *)getConfig {
// Create config with your credentials
SingularConfig *config = [[SingularConfig alloc]
initWithApiKey:@"YOUR_SDK_KEY"
andSecret:@"YOUR_SDK_SECRET"];
// OPTIONAL: Wait for ATT consent (if showing ATT prompt)
// Remove this line if NOT using App Tracking Transparency
config.waitForTrackingAuthorizationWithTimeoutInterval = 300;
// OPTIONAL: Support custom ESP domains for deep links
config.espDomains = @[@"links.your-domain.com"];
// OPTIONAL: Handle deep links
config.singularLinksHandler = ^(SingularLinkParams *params) {
[self handleDeeplink:params];
};
return config;
}
#pragma mark - OPTIONAL: Deep link handler implementation
- (void)handleDeeplink:(SingularLinkParams *)params {
// Guard clause: Exit if params is nil
if (!params) {
return;
}
// Guard clause: Exit if no deep link provided
NSString *deeplink = [params getDeepLink];
if (!deeplink) {
return;
}
// Extract deep link parameters
NSString *passthrough = [params getPassthrough];
BOOL isDeferred = [params isDeferred];
NSDictionary *urlParams = [params getUrlParameters];
#ifdef DEBUG
// Debug logging only - stripped from production builds
NSLog(@"Singular Links Handler");
NSLog(@"Singular deeplink received: %@", deeplink);
NSLog(@"Singular passthrough received: %@", passthrough);
NSLog(@"Singular isDeferred received: %@", isDeferred ? @"YES" : @"NO");
NSLog(@"Singular URL Params received: %@", urlParams);
#endif
// TODO: Navigate to appropriate screen based on deep link
// Add deep link handling code here. Navigate to appropriate screen.
}
SKAdNetwork Auto-Enabled: A partir de la versión 12.0.6 del SDK, SKAdNetwork está habilitado por defecto. No es necesaria ninguna configuración adicional.
Inicializar SDK
Elija la arquitectura de su aplicación
Inicialice el SDK en cada punto de entrada de la aplicación. El patrón de inicialización depende de la arquitectura de su aplicación.
¿Qué arquitectura tengo?
-
SceneDelegate: Comprueba si tu proyecto tiene
SceneDelegate.swiftoSceneDelegate.m -
SwiftUI: Tu app comienza con
@main struct YourApp: App - Sólo AppDelegate: Aplicaciones anteriores a iOS 13 o aplicaciones sin SceneDelegate
iOS moderno: SceneDelegate (iOS 13+)
Dónde añadir código: SceneDelegate.swift
o SceneDelegate.m
Puntos de entrada a inicializar:
-
willConnectTo session- Lanzamiento de la aplicación -
continue userActivity- Enlaces universales -
openURLContexts- Esquemas de enlaces profundos
import Singular
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
// 1️⃣ App launch
func scene(_ scene: UIScene, willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions) {
// ANTI-SWIZZLING: Capture deep link parameters IMMEDIATELY before any
// other code runs. This prevents third-party SDKs from intercepting
// or modifying these values.
let userActivity = connectionOptions.userActivities.first
let urlContext = connectionOptions.urlContexts.first
let openUrl = urlContext?.url
#if DEBUG
// Log captured values to detect swizzling interference
print("[SWIZZLE CHECK] UserActivity captured:", userActivity?.webpageURL?.absoluteString ?? "none")
print("[SWIZZLE CHECK] URL Context captured:", openUrl?.absoluteString ?? "none")
print("IDFV:", UIDevice.current.identifierForVendor?.uuidString ?? "N/A")
#endif
// Create window from windowScene
guard let windowScene = scene as? UIWindowScene else { return }
window = UIWindow(windowScene: windowScene)
window?.rootViewController = UIViewController() // Replace with your root VC
window?.makeKeyAndVisible()
// Singular initialization - uses captured values to avoid swizzling conflicts
guard let config = getConfig() else { return }
// Pass Universal Link if available
if let userActivity = userActivity {
config.userActivity = userActivity
}
// Pass URL scheme if available
// CRITICAL for custom URL scheme attribution
if let openUrl = openUrl {
config.openUrl = openUrl
}
// Initialize Singular SDK
Singular.start(config)
}
// 2️⃣ Universal Links
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
guard let config = getConfig() else { return }
config.userActivity = userActivity
// Initialize Singular SDK
Singular.start(config)
}
// 3️⃣ Deep link schemes
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
guard let config = getConfig() else { return }
if let url = URLContexts.first?.url {
config.openUrl = url
}
// Initialize Singular SDK
Singular.start(config)
}
}
#import <Singular/Singular.h>
@implementation SceneDelegate
// 1️⃣ App launch
- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session
options:(UISceneConnectionOptions *)connectionOptions {
// ANTI-SWIZZLING: Capture deep link parameters IMMEDIATELY before any
// other code runs. This prevents third-party SDKs from intercepting
// or modifying these values.
NSUserActivity *userActivity =
[[[connectionOptions userActivities]
allObjects] firstObject];
UIOpenURLContext *urlContext =
[[connectionOptions URLContexts]
allObjects].firstObject;
NSURL *openUrl = urlContext.URL;
#ifdef DEBUG
// Log captured values to detect swizzling interference
NSLog(@"[SWIZZLE CHECK] "
"UserActivity captured: %@",
userActivity.webpageURL);
NSLog(@"[SWIZZLE CHECK] "
"URL Context captured: %@",
openUrl);
#endif
// Create window from windowScene
UIWindowScene *windowScene = (UIWindowScene *)scene;
self.window = [[UIWindow alloc] initWithWindowScene:windowScene];
self.window.rootViewController = [[UIViewController alloc] init];
[self.window makeKeyAndVisible];
#ifdef DEBUG
// Print IDFV for testing in SDK Console
NSLog(@"IDFV: %@", [[[UIDevice currentDevice] identifierForVendor] UUIDString]);
#endif
// Singular initialization - uses captured values to avoid swizzling
SingularConfig *config = [self getConfig];
// Pass Universal Link if available
if (userActivity) {
config.userActivity = userActivity;
}
// Pass URL scheme if available
// CRITICAL for custom URL scheme
if (openUrl) {
config.openUrl = openUrl;
}
// Initialize Singular SDK
[Singular start:config];
}
// 2️⃣ Universal Links
- (void)scene:(UIScene *)scene continueUserActivity:(NSUserActivity *)userActivity {
// ANTI-SWIZZLING: Capture userActivity immediately at method entry. Called
// when Universal Link is opened while app is running or backgrounded.
NSUserActivity *capturedActivity = userActivity;
#ifdef DEBUG
NSLog(@"[SWIZZLE CHECK] "
"continueUserActivity captured: %@",
capturedActivity.webpageURL);
#endif
SingularConfig *config = [self getConfig];
config.userActivity = capturedActivity;
// Initialize Singular SDK
[Singular start:config];
}
// 3️⃣ Deep link schemes
- (void)scene:(UIScene *)scene openURLContexts:(NSSet *)URLContexts {
// ANTI-SWIZZLING: Capture URL immediately at method entry. Called
// when URL scheme is opened while app is running or backgrounded.
NSURL *capturedUrl =
[[URLContexts allObjects]
firstObject].URL;
#ifdef DEBUG
NSLog(@"[SWIZZLE CHECK] "
"openURLContexts captured: %@",
capturedUrl);
#endif
SingularConfig *config = [self getConfig];
if (capturedUrl) {
config.openUrl = capturedUrl;
}
// Initialize Singular SDK
[Singular start:config];
}
@end
iOS moderno: SwiftUI App (iOS 14+)
Dónde añadir el código: Tu archivo principal Appstruct
Puntos de entrada para inicializar:
-
.onOpenURL(of: scenePhase)- Manejar esquemas de URL personalizados -
.onContinueUserActivity(of: scenePhase)- Manejar enlaces universales (enlaces profundos singulares) -
.onChange.active- Maneja la inicialización en el primer lanzamiento si no ocurrió ningún enlace profundo. Maneja Deeplinks diferidos.
import SwiftUI
import Singular
@main
struct simpleSwiftUIApp: App {
@Environment(\.scenePhase) var scenePhase
@State private var hasInitialized = false
var body: some Scene {
WindowGroup {
ContentView()
// 1️⃣ Handle custom URL schemes (e.g., myapp://path)
.onOpenURL { url in
#if DEBUG
print("[Singular] URL Scheme:", url.absoluteString)
#endif
guard let config = getConfig() else { return }
config.openUrl = url
Singular.start(config)
hasInitialized = true
}
// 2️⃣ Handle Universal Links (e.g., https://links.your-domain.com)
.onContinueUserActivity(NSUserActivityTypeBrowsingWeb) { userActivity in
#if DEBUG
print("[Singular] Universal Link:", userActivity.webpageURL?.absoluteString ?? "none")
#endif
guard let config = getConfig() else { return }
config.userActivity = userActivity
Singular.start(config)
hasInitialized = true
}
}
.onChange(of: scenePhase) { oldPhase, newPhase in
switch newPhase {
case .active:
// 3️⃣ Initialize ONLY on first launch if no deep link occurred
guard !hasInitialized else {
#if DEBUG
print("[Singular] Already initialized, skipping")
#endif
return
}
#if DEBUG
if let idfv = UIDevice.current.identifierForVendor?.uuidString {
print("[Singular] IDFV:", idfv)
}
#endif
guard let config = getConfig() else { return }
Singular.start(config)
hasInitialized = true
case .background:
#if DEBUG
print("[Singular] App backgrounded")
#endif
case .inactive:
#if DEBUG
print("[Singular] App inactive")
#endif
@unknown default:
break
}
}
}
// Add your getConfig() function here
// MARK: - Singular Configuration
private func getConfig() -> SingularConfig? {
// ... (same as above)
}
// Add your handleDeeplink() function here
// MARK: - Deep Link Handler
private func handleDeeplink(_ params: SingularLinkParams) {
// ... (same as above)
}
}
iOS heredado: AppDelegate (Pre-iOS 13)
Dónde añadir el código: AppDelegate.swift
o AppDelegate.m
Puntos de entrada a inicializar:
-
didFinishLaunchingWithOptions- Lanzamiento de la aplicación -
continue userActivity- Enlaces universales -
open url- Esquemas de enlaces profundos
import Singular
import UIKit
class AppDelegate: UIResponder, UIApplicationDelegate {
// 1️⃣ App launch
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// ANTI-SWIZZLING: Capture deep link parameters IMMEDIATELY before any
// other code runs. This prevents third-party SDKs from intercepting
// or modifying these values.
let launchUrl = launchOptions?[.url] as? URL
let userActivityDictionary = launchOptions?[.userActivityDictionary] as? [String: Any]
let userActivity = userActivityDictionary?["UIApplicationLaunchOptionsUserActivityKey"] as? NSUserActivity
#if DEBUG
// Log captured values to detect swizzling interference
print("[SWIZZLE CHECK] Launch URL captured:", launchUrl?.absoluteString ?? "none")
print("[SWIZZLE CHECK] UserActivity captured:", userActivity?.webpageURL?.absoluteString ?? "none")
print("IDFV:", UIDevice.current.identifierForVendor?.uuidString ?? "N/A")
#endif
// Singular initialization - uses captured values to avoid swizzling conflicts
guard let config = getConfig() else { return true }
// Pass the entire launchOptions dictionary for Singular's internal processing
config.launchOptions = launchOptions
// Explicitly pass Universal Link if available
// CRITICAL for universal link attribution
if let userActivity = userActivity {
config.userActivity = userActivity
}
// Explicitly pass URL scheme if available
// CRITICAL for custom URL scheme attribution
if let launchUrl = launchUrl {
config.openUrl = launchUrl
}
// Initialize Singular SDK
Singular.start(config)
return true
}
// 2️⃣ Universal Links
func application(_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: @escaping ([any UIUserActivityRestoring]?) -> Void) -> Bool {
#if DEBUG
print("[SWIZZLE CHECK] Universal Link handler called:", userActivity.webpageURL?.absoluteString ?? "none")
#endif
guard let config = getConfig() else { return true }
config.userActivity = userActivity
Singular.start(config)
return true
}
// 3️⃣ Deep link schemes
func application(_ app: UIApplication,
open url: URL,
options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
#if DEBUG
print("[SWIZZLE CHECK] URL Scheme handler called:", url.absoluteString)
#endif
guard let config = getConfig() else { return true }
config.openUrl = url
Singular.start(config)
return true
}
// Add your getConfig() function here
// MARK: - Singular Configuration
private func getConfig() -> SingularConfig? {
// ... (same as above)
}
// Add your handleDeeplink() function here
// MARK: - Deep Link Handler
private func handleDeeplink(_ params: SingularLinkParams) {
// ... (same as above)
}
}
#import "AppDelegate.h"
#import <Singular/Singular.h>
@implementation AppDelegate
// 1️⃣ App launch
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// ANTI-SWIZZLING: Capture deep link parameters IMMEDIATELY before any
// other code runs. This prevents third-party SDKs from intercepting
// or modifying these values.
NSURL *launchUrl = [launchOptions objectForKey:UIApplicationLaunchOptionsURLKey];
NSDictionary *userActivityDictionary = [launchOptions objectForKey:UIApplicationLaunchOptionsUserActivityDictionaryKey];
NSUserActivity *userActivity = [userActivityDictionary objectForKey:@"UIApplicationLaunchOptionsUserActivityKey"];
#if DEBUG
// Log captured values to detect swizzling interference
NSLog(@"[SWIZZLE CHECK] Launch URL captured: %@", launchUrl.absoluteString ?: @"none");
NSLog(@"[SWIZZLE CHECK] UserActivity captured: %@", userActivity.webpageURL.absoluteString ?: @"none");
NSLog(@"IDFV: %@", [UIDevice currentDevice].identifierForVendor.UUIDString ?: @"N/A");
#endif
// Singular initialization - uses captured values to avoid swizzling conflicts
SingularConfig *config = [self getConfig];
if (!config) {
return YES;
}
// Pass the entire launchOptions dictionary for Singular's internal processing
config.launchOptions = launchOptions;
// Explicitly pass Universal Link if available
// CRITICAL for universal link attribution
if (userActivity) {
config.userActivity = userActivity;
}
// Explicitly pass URL scheme if available
// CRITICAL for custom URL scheme attribution
if (launchUrl) {
config.openUrl = launchUrl;
}
// Initialize Singular SDK
[Singular start:config];
return YES;
}
// 2️⃣ Universal Links
- (BOOL)application:(UIApplication *)application
continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler {
#if DEBUG
NSLog(@"[SWIZZLE CHECK] Universal Link handler called: %@", userActivity.webpageURL.absoluteString ?: @"none");
#endif
SingularConfig *config = [self getConfig];
if (!config) {
return YES;
}
config.userActivity = userActivity;
[Singular start:config];
return YES;
}
// 3️⃣ Deep link schemes
- (BOOL)application:(UIApplication *)app
openURL:(NSURL *)url
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
#if DEBUG
NSLog(@"[SWIZZLE CHECK] URL Scheme handler called: %@", url.absoluteString);
#endif
SingularConfig *config = [self getConfig];
if (!config) {
return YES;
}
config.openUrl = url;
[Singular start:config];
return YES;
}
#pragma mark - Singular Configuration
- (SingularConfig *)getConfig {
}
#pragma mark - OPTIONAL: Deep link handler implementation
- (void)handleDeeplink:(SingularLinkParams *)params {
}
@end
Verificar la instalación
Lista de comprobación previa al vuelo
Confirme estos elementos antes de crear y probar su integración.
- SDK instalado a través de CocoaPods, SPM o framework manual
- Cabecera de puente Swift creada (si se utiliza Swift)
-
getConfig()función implementada -
Singular.start(config)llamada en todos los puntos de entrada - SDK Key y SDK Secret añadidos a la configuración
- Tiempo de espera ATT configurado (sólo si se muestra el mensaje ATT)
- Gestor de enlaces profundos configurado (sólo si se utilizan enlaces profundos)
- La aplicación se compila sin errores
Siguientes pasos:
- Cree y ejecute su aplicación
- Compruebe la consola para la declaración de impresión IDFV
- Pruebe en Singular SDK Consoleutilizando su IDFV
- Compruebe que las sesiones aparecen en SDK Console en 1-2 minutos
Opcional: App Tracking Transparency (ATT)
Configure ATT para solicitar permiso al usuario para acceder a IDFA y mejorar la precisión de la atribución.
Omita esta sección si: No está mostrando un aviso ATT en su app.
¿Por qué solicitar el consentimiento ATT?
Ventajas del IDFA
A partir de iOS 14.5, las apps deben solicitar permiso al usuario para acceder al IDFA (Identificador para Anunciantes) del dispositivo.
Atribución con y sin IDFA:
- Con IDFA
- : Atribución precisa a nivel de dispositivo y coincidencia exacta de instalación
- Sin IDFA: Atribución probabilística utilizando IP, agente de usuario y huella digital de dispositivo
Recomendación: Solicitar el consentimiento de la ATT para mejorar la precisión de la atribución. Singular puede atribuir sin IDFA, pero la precisión disminuye.
Configurar Retraso ATT
Retraso Inicialización SDK
Añada un tiempo de espera para esperar la respuesta ATT del usuario antes de enviar la primera sesión a Singular.
Crítico: El SDK debe esperar el consentimiento ATT antes de enviar la primera sesión. De lo contrario, el evento de atribución inicial no incluirá el IDFA.
func getSingularConfig() -> SingularConfig? {
guard let config = SingularConfig(
apiKey: "YOUR_SDK_KEY",
andSecret: "YOUR_SDK_SECRET"
) else {
return nil
}
// Wait up to 300 seconds for ATT response
config.waitForTrackingAuthorizationWithTimeoutInterval = 300
return config
}
- (SingularConfig *)getSingularConfig {
SingularConfig *config = [[SingularConfig alloc]
initWithApiKey:@"YOUR_SDK_KEY"
andSecret:@"YOUR_SDK_SECRET"];
// Wait up to 300 seconds for ATT response
config.waitForTrackingAuthorizationWithTimeoutInterval = 300;
return config;
}
Cronología del flujo ATT
Esto es lo que ocurre cuando se configura un retraso ATT:
- Lanzamiento de la aplicación: SDK comienza a registrar eventos pero aún no los envía
- Aviso ATT: Su aplicación muestra el diálogo de consentimiento de ATT
- Respuesta del usuario: El usuario concede o deniega el permiso
- SDK envía datos: El SDK envía inmediatamente los eventos en cola con IDFA (si se concede)
- Tiempo de espera: Si pasan 300 segundos sin respuesta, el SDK envía los datos de todas formas.
Mejor práctica: Muestra tu aviso ATT lo antes posible (idealmente en el primer lanzamiento de la app) para maximizar la disponibilidad de IDFA para la atribución.