Requisitos previos
Completa los pasos en Integrar un SDK de Singular: Planificación y requisitos previos antes de continuar con esta integración.
Importante: Estos pasos previos son obligatorios para cualquier integración del SDK de Singular.
Configuración específica para iOS
Antes de inicializar el SDK, confirma que los siguientes elementos estén configurados en tu proyecto de Xcode. La falta de cualquiera de ellos es una causa común de fallos silenciosos del SDK.
Entradas en Info.plist:
-
NSUserTrackingUsageDescription— Una cadena visible para el usuario que explica por qué tu aplicación solicita tracking. Requerida por Apple si alguna vez presentas el aviso de App Tracking Transparency (ATT) o lees el IDFA. El SDK de Singular respeta ATT automáticamente; sin esta clave el SDK sigue funcionando, pero no puede acceder al IDFA en iOS 14.5+. -
URL Types y
CFBundleURLSchemes— Requeridos si tu estrategia de deep linking usa esquemas URI personalizados (por ejemplo,myapp://). No son necesarios para Universal Links / Singular Links vía HTTPS.
Entitlements (Signing & Capabilities):
-
Associated Domains
— Agrega una entrada por cada dominio de marca Singular con el formato
applinks:yourcompany.sng.link. Requerido para la atribución de Universal Link / Singular Link. Este dominio alojado por Singular hospedará un archivo JSONapple-app-site-association(AASA) que Apple descarga en el momento de la instalación. El archivo AASA se genera y se actualiza cuando se crean enlaces para la aplicación en la plataforma de Singular. - Push Notifications — Requerido para el tracking de desinstalaciones y la re-interacción atribuida vía push. Consulta los artículos sobre Supporting Push Notifications y Uninstall Tracking.
Frameworks del sistema requeridos (solo para integración manual — CocoaPods y SwiftPM los enlazan automáticamente):
-
StoreKit.framework— IAP y SKAdNetwork. -
AdServices.framework— Token de atribución de Apple Search Ads (iOS 14.3+). -
AppTrackingTransparency.framework— Aviso de ATT y estado de autorización (iOS 14.5+). -
UserNotifications.framework— Notificaciones push. -
WebKit.framework— Requerido solo si usas el puente JS de WKWebView de Singular.
Instalación
Elige tu método de instalación preferido. Recomendamos CocoaPods para la mayoría de los proyectos.
Decisión rápida:
- ¿Ya usas CocoaPods? Usa el Método 1
- ¿Proyecto solo con SPM? Usa el Método 2
- ¿Sin gestor de paquetes? Usa el Método 3
Métodos de instalación
Método 1: CocoaPods (recomendado)
Requisitos:
- CocoaPods instalado ( guía de instalación )
- Acceso a la terminal en el directorio de tu proyecto
Pasos de instalación:
-
Inicializa el Podfile (omite este paso si ya tienes uno):
cd /path/to/your/project pod init -
Agrega el SDK de Singular a tu Podfile:
platform :ios, '12.0' target 'YourAppName' do use_frameworks! # Singular SDK pod 'Singular-SDK' end -
Instala las dependencias:
pod install -
Abre el workspace:
A partir de ahora, abre
.xcworkspaceen lugar de.xcodeproj - Solo proyectos Swift: Crea un bridging header (consulta más abajo)
Método 2: Swift Package Manager
Pasos de instalación:
- En Xcode: File → Add Packages
-
Ingresa la URL del repositorio:
https://github.com/singular-labs/Singular-iOS-SDK - Selecciona la versión y haz clic en Add Package
-
Agrega los frameworks requeridos:
Ve a Build Phases → Link Binary with Libraries y agrega:-
Enlaza las librerías requeridas:
Ve a Build Phases → Link Binary With Libraries y agrega:- Libsqlite3.0.tbd
- SystemConfiguration.framework
- Security.framework
- Libz.tbd
- AdSupport.framework
- WebKit.framework
- StoreKit.framework
- AdServices.framework (marcar como Optional)
-
Enlaza las librerías requeridas:
- Solo proyectos Swift: Crea un bridging header (consulta más abajo)
Método 3: Instalación manual del framework
Cuándo usarlo: Usa este método solo si no puedes usar CocoaPods o SPM.
Descargar framework:
- Xcode 12+: Descargar .xcframework
- Xcode 11 e inferiores: Descargar .framework
Pasos de instalación:
- Descomprime el framework descargado
- En Xcode: clic derecho en el proyecto → Add Files To [Project]
- Selecciona Create Groups y agrega la carpeta del framework
-
Enlaza las librerías requeridas:
Ve a Build Phases → Link Binary With Libraries y agrega:- Libsqlite3.0.tbd
- SystemConfiguration.framework
- Security.framework
- Libz.tbd
- AdSupport.framework
- WebKit.framework
- StoreKit.framework
- AdServices.framework (marcar como Optional)
-
Incrusta el framework:
Ve a General → Frameworks, Libraries, and Embedded Content
Configura el framework Singular como Embed & Sign
Bridging Header de Swift
Importante: Requerido para proyectos Swift que usen CocoaPods o SPM.
-
Crea el archivo header:
Xcode → File → New → File → Header File
NómbraloYourProjectName-Bridging-Header.h -
Agrega el import:
#import <Singular/Singular.h> -
Enlázalo en los build settings:
Build Settings → Objective-C Bridging Header
Configúralo como:YourProjectName/YourProjectName-Bridging-Header.h
Configurar e inicializar el SDK
Crea un objeto de configuración e inicializa el SDK en los puntos de entrada de tu aplicación.
Crear el objeto de configuración
Configuración básica
Crea un objeto
SingularConfig
con tus credenciales del SDK y funciones opcionales. Esta configuración es universal para todas las arquitecturas de aplicación.
Obtén tus credenciales: Encuentra tu SDK Key y SDK Secret en la plataforma de Singular en Developer Tools → SDK Integration .
// 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 habilitado automáticamente: A partir de la versión 12.0.6 del SDK, SKAdNetwork está habilitado de forma predeterminada. No se necesita configuración adicional.
Inicializar el SDK
Elige la arquitectura de tu aplicación
Inicializa el SDK en cada punto de entrada de la aplicación. El patrón de inicialización depende de la arquitectura de tu aplicación.
¿Qué arquitectura tengo?
-
SceneDelegate:
Verifica si tu proyecto tiene
SceneDelegate.swiftoSceneDelegate.m -
SwiftUI:
Tu aplicación comienza con
@main struct YourApp: App - Solo AppDelegate: Aplicaciones previas a iOS 13 o aplicaciones sin SceneDelegate
iOS moderno: SceneDelegate (iOS 13+)
Dónde agregar el código:
SceneDelegate.swift
o
SceneDelegate.m
Puntos de entrada para inicializar:
-
willConnectTo session- Lanzamiento de la aplicación -
continue userActivity- Universal Links -
openURLContexts- Esquemas de deep link
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: aplicación SwiftUI (iOS 14+)
Dónde agregar el código:
El archivo de tu struct principal
App
Puntos de entrada para inicializar:
-
.onOpenURL(of: scenePhase)- Manejar esquemas URL personalizados -
.onContinueUserActivity(of: scenePhase)- Manejar Universal Links (Singular Deep Links) -
.onChange.active- Manejar la inicialización en el primer lanzamiento si no hubo deep link. Gestiona los Deferred Deeplinks.
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 (anterior a iOS 13)
Dónde agregar el código:
AppDelegate.swift
o
AppDelegate.m
Puntos de entrada para inicializar:
-
didFinishLaunchingWithOptions- Lanzamiento de la aplicación -
continue userActivity- Universal Links -
open url- Esquemas de deep link
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 verificación previa
Confirma estos elementos antes de compilar y probar tu integración.
- SDK instalado vía CocoaPods, SPM o framework manual
- Bridging header de Swift creado (si usas Swift)
-
Función
getConfig()implementada -
Singular.start(config)llamado en todos los puntos de entrada - SDK Key y SDK Secret agregados a la configuración
- Timeout de ATT configurado (solo si se muestra el aviso de ATT)
- Manejador de deep link configurado (solo si usas deep links)
- La aplicación compila sin errores
Siguientes pasos:
- Compila y ejecuta tu aplicación
- Revisa la consola para ver la sentencia print del IDFV
- Prueba en la Singular SDK Console usando tu IDFV
- Verifica que las sesiones aparezcan en la SDK Console en 1-2 minutos
Opcional: App Tracking Transparency (ATT)
Configura ATT para solicitar permiso al usuario para acceder al IDFA y mejorar la precisión de la atribución.
Omite esta sección si: No vas a mostrar un aviso de ATT en tu aplicación.
¿Por qué solicitar el consentimiento ATT?
Beneficios del IDFA
A partir de iOS 14.5, las aplicaciones deben solicitar permiso al usuario para acceder al IDFA (Identifier for Advertisers) del dispositivo.
Atribución con vs. sin IDFA:
- Con IDFA: Atribución precisa a nivel de dispositivo y matching exacto de instalaciones
- Sin IDFA: Atribución probabilística usando IP, user agent y fingerprinting de dispositivo
Recomendación: Solicita el consentimiento ATT para obtener mejor precisión en la atribución. Singular puede atribuir sin IDFA, pero la precisión disminuye.
Configurar el retraso de ATT
Retrasar la inicialización del SDK
Agrega un timeout para esperar la respuesta de ATT del usuario antes de enviar la primera sesión a Singular.
Crítico: El SDK debe esperar el consentimiento de ATT antes de enviar la primera sesión. De lo contrario, el evento inicial de atribución 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 de ATT
Esto es lo que sucede cuando configuras un retraso de ATT:
- Lanzamiento de la aplicación: El SDK comienza a registrar eventos, pero aún no los envía
- Aviso de ATT: Tu aplicación muestra el diálogo de consentimiento de ATT
- Respuesta del usuario: El usuario otorga o niega el permiso
- El SDK envía los datos: El SDK envía inmediatamente los eventos en cola con IDFA (si se otorgó)
- Tiempo de espera de respaldo: Si pasan 300 segundos sin respuesta, el SDK envía los datos de todos modos
Mejor práctica: Muestra el aviso de ATT lo antes posible (idealmente en el primer lanzamiento de la aplicación) para maximizar la disponibilidad del IDFA para la atribución.