Guía de implantación de SKAdNetwork 4

Guía de implantación de SKAdNetwork 4

Caso práctico de SKAdNetwork

SKAdNetwork (SKAN) es el marco de atribución de Apple centrado en la privacidad que permite medir las campañas publicitarias de instalación de aplicaciones iOS protegiendo la privacidad del usuario. La implementación de servidor a servidor (S2S) proporciona una forma sólida de validar y realizar un seguimiento del rendimiento de la campaña mediante el envío de datos de SKAdNetwork directamente entre servidores, lo que garantiza una atribución precisa y un seguimiento de la conversión.

El marco gestiona todos los aspectos críticos de la atribución al tiempo que mantiene la privacidad del usuario a través de los métodos prescritos por Apple, por lo que es una herramienta esencial para los vendedores móviles que operan en el paisaje post-iOS 14.5.

Puntos clave

  • Validación y agregación sólidas de las devoluciones de todas las redes con protección contra el fraude integrada.
  • Gestión dinámica del valor de conversión mediante la configuración del panel de control
  • Capacidades de generación de informes mejoradas con parámetros de marketing enriquecidos y perspectivas de datos granulares
  • Sistema seguro de devoluciones de socios para valores de conversión descodificados y seguimiento de ingresos
  • Validación exhaustiva de la implementación en el lado del cliente mediante el seguimiento enriquecido de eventos y sesiones
  • Gestión automatizada de marcas de tiempo a través de múltiples ventanas de postback (0-2 días, 3-7 días, 8-35 días)
  • Compatibilidad con la monetización de anuncios y el seguimiento de ingresos normales con especificaciones de divisas

Requisitos previos

Componentes clave

La solución SKAdNetwork de Singular consta de los siguientes componentes:

  • Código del lado del cliente para implementar SKAdNetwork.
    Existe un enfoque alternativo del lado del servidor, que utiliza el punto final API de valor de conversión.
  • Validación y agregación postback de todas las redes
  • Protección contra el fraude:
    • Validación de firmas y deduplicación de ID de transacciones.
    • Configuración segura con las redes para verificar los parámetros que no están firmados (valor de conversión y geodatos).
  • Gestión del valor de conversión: ofrece la posibilidad de configurar dinámicamente en el panel de control de Singular qué actividad posterior a la instalación debe codificarse en el valor de conversión de SKAdNetwork.
  • Elaboración de informes: Traducción del ID de campaña limitado de SKAdNetwork y enriquecimiento de los datos con más parámetros y granularidades de marketing.
  • Postbacks de socios: Proporciona el envío de postbacks de SKAdNetwork con un valor de conversión descodificado en eventos e ingresos, lo cual es crítico para su optimización.

La implementación de SKAdNetwork del lado del cliente consta de dos partes principales:

  1. Implementación del lado del cliente SKAdNetwork: Esta parte es fundamental para registrar sus aplicaciones en SKAdNetwork y gestionar el valor de conversión de SKAdNetwork de forma inteligente. Esto significa que, al implementarla, podrá optimizar sus campañas basándose en las atribuciones de SKAdNetwork y sus actividades post-instalación asociadas.
  2. Actualización de la integración del lado del servidor: Esta parte es importante para validar y solucionar problemas de la implementación del lado del cliente. Al enriquecer los eventos y sesiones enviados a Singular a través de los endpoints Sesión y Evento con metadatos de SKAdNetwork, Singular puede validar que la implementación se ha realizado correctamente en el lado de su aplicación.

Primeros pasos


Implementación de SKAdNetwork en el lado del cliente

Singular proporciona fragmentos de código de interfaz SKAdNetwork que permiten registrarse en SKAdNetwork y gestionar el valor de conversión. Estos ejemplos de código se encargan de las siguientes partes:

  1. Soporte y registro de SKAdNetwork
  2. Gestión del valor de conversión:
    • El código se comunica de forma sincrónica con el endpoint de Singular para recibir el siguiente valor de conversión basado en el modelo de conversión configurado. Informa de los eventos/sesiones/ingresos y, en respuesta, obtiene el siguiente valor de conversión, que es un número codificado que representa la actividad posterior a la instalación que se configuró para la medición en el panel de control de Singular.
    • El código también recopila metadatos de SKAdnetwork por periodo de medición. Los metadatos se utilizan tanto para la validación como para el cálculo del siguiente valor de conversión:
      • La marca de tiempo de la primera llamada al marco SKAdNetwork subyacente.
      • La fecha y hora de la última llamada al marco SKAdNetwork subyacente.
      • El último valor de postbacks actualizado (tanto grueso como fino)
      • Ingresos totales e ingresos totales de Admon generados por el dispositivo

Flujo de integración

Screen_Shot_2020-09-16_at_18.59.13.png

El diagrama anterior ilustra el flujo de SKAdNetwork para un cliente S2S:

  1. En primer lugar, el código de la aplicación se comunica con un servidor Singular (a través de un punto final dedicado para SKAdNetwork) para obtener el último valor de conversión de forma sincrónica en función de los eventos/sesiones/eventos de ingresos que se producen en la aplicación y actualizar el marco SKAdNetwork con este valor.
  2. En segundo lugar, la aplicación enriquece los eventos y sesiones existentes con datos de SKAdNetwork, que se utilizarán posteriormente para las validaciones.
  3. Cuando la aplicación termina de actualizar el marco SKAdNetwork con los nuevos valores de conversión y el temporizador SKAdNetwork expira, el postback SKAdNetwork se envía a la red.
  4. La red lo reenviará a Singular (a través de la configuración segura o de la configuración normal).
  5. Singular procesará el postback
    • Validando su firma
    • Decodificación del valor de conversión basado en el modelo de conversión configurado.
    • Enriquecer el postback con información de la red. Los datos se recogen de las integraciones con los socios uniendo el SKAdNetwork y el ID de campaña de la red.
    • Envío de los postbacks descodificados a BI y a los socios

Una nota importante es que la información de SKAdNetwork, incluidas las instalaciones y los eventos descodificados, será accesible a través de un conjunto diferente de informes/APIs/ tablas ETL y postbacks para evitar mezclarla con sus conjuntos de datos existentes. Esto es especialmente importante durante las próximas semanas para que pueda medir y probar SKAdNetwork junto con su campaña actual.

Interfaz de SKAdNetwork

Este archivo de cabecera define la interfaz pública para la integración de SKAdNetwork (SKAN), proporcionando métodos para el seguimiento de atribuciones, actualizaciones de valores de conversión y gestión de ingresos en aplicaciones iOS.

SKANSnippet.h Ejemplo de código

Registro de atribución

Inicializa el seguimiento de atribución de SKAN en el primer lanzamiento de la aplicación, estableciendo el valor de conversión inicial en 0 y estableciendo marcas de tiempo de referencia. El método subyacente de la API de Apple genera una notificación si el dispositivo tiene datos de atribución para esa aplicación.

Objective-C
+ (void)registerAppForAdNetworkAttribution;

Gestión del valor de conversión

  • El valor de conversión se calcula en función de la actividad posterior a la instalación de un dispositivo que se captura mediante los métodos que se indican a continuación y un modelo de conversión seleccionado que el usuario puede configurar dinámicamente.
  • Los métodos de esta sección se encargan de extraer el siguiente valor de conversión del endpoint de Singular en función del modelo de conversión seleccionado y de la actividad post-instalación notificada (véase la documentación anterior).
  • Los métodos siguientes actualizan el valor de conversión en función de las siguientes actividades posteriores a la instalación:
    • Sesión: crítico para la medición de retención de un usuario con SKAdNetwork.
    • Evento deconversión: crítico para la medición de eventos de conversión post-instalación con SKAdNetwork
    • Evento de ingresos: crítico para la medición de ingresos con SKAdNetwork

Seguimiento de sesión

Gestiona el seguimiento basado en sesiones para la retención y el análisis de cohortes, con gestor de finalización opcional para acciones posteriores a la actualización.

Objective-C
+ (void)updateConversionValuesAsync:(void(^)(NSNumber *, NSNumber *, BOOL, NSError *))handler;

Seguimiento de eventos

Gestiona el seguimiento de eventos de conversión antes de enviar los datos a Singular, actualizando los valores de conversión en función del contexto del evento.

Objective-C
+ (void)updateConversionValuesAsync:(NSString *)eventName 
                withCompletionHandler:(void(^)(NSNumber *, NSNumber *, BOOL, NSError *))handler;

Gestión de ingresos

Realiza un seguimiento de los eventos de ingresos, manteniendo totales separados para la monetización de anuncios y los ingresos normales, y solicitando una llamada antes de actualizar los valores de conversión.

Objective-C
+ (void)updateRevenue:(double)amount 
          andCurrency:(NSString *)currency 
     isAdMonetization:(BOOL)admon;

Recuperación de datos

Devuelve datos completos de SKAN, incluyendo

  • Valores de conversión detallados actuales y anteriores
  • Valores gruesos a través de diferentes ventanas de postback
  • Marcas de tiempo de bloqueo de ventanas
  • Seguimiento de ingresos por divisa
  • Seguimiento separado de la monetización de anuncios y de los ingresos ordinarios
Objective-C
+ (NSDictionary *)getSkanDetails;

Notas de implementación

  • Los métodos utilizan patrones asíncronos para evitar el bloqueo del hilo principal
  • El seguimiento de los ingresos debe preceder a las actualizaciones de los valores de conversión
  • Admite valores de conversión de grano fino (0-63) y grueso (bajo/medio/alto).
  • Mantiene un seguimiento separado para las diferentes ventanas de postback
  • Implementa una gestión exhaustiva de errores mediante gestores de finalización

Código de la interfaz SKANSnippet.h

Objective-C
//SKANSnippet.h

#import <Foundation/Foundation.h>

@interface SKANSnippet : NSObject

// Register for SKAdNetwork attribution.

// You should call this method as soon as possible once the app is launched for the first time.

// This function sets the conversion value to be 0 and updates the timestamp for additional processing.

+ (void)registerAppForAdNetworkAttribution;

// To track retention and cohorts you need to call this method for each app open.

// It reports the session details and updates the conversion value due to this session if needed.

// The callback passed to the method is optional, you can use it to run code once the conversion value is updated.

+ (void)updateConversionValuesAsync:(void(^)(NSNumber *, NSNumber *, BOOL, NSError *))handler;

// To track conversion events with SKAdNetwork you need to call this method after each event and before this event is sent to Singular.

// It reports the event details and updates the conversion value due to this event if needed.

// The callback passed to the method is optional, you can use it to run code once the conversion value is updated.

+ (void)updateConversionValuesAsync:(NSString *)eventName withCompletionHandler:(void(^)(NSNumber *, NSNumber *, BOOL, NSError *))handler;

// To track revenue with SKAdNetwork you need to call this method before every revenue event.

// It will update the total revenue, so when you call 'updateConversionValuesAsync', the new conversion value will be determined according to the total amout of revenue.

// Note:

// 1. Call this method before calling 'updateConversionValuesAsync' to make sure that revenue is updated.

// 2. In case of retrying an event, avoid calling this method so the same revenue will not be counted twice.

+ (void)updateRevenue:(double)amount andCurrency:(NSString *)currency isAdMonetization:(BOOL)admon;

// Gets current fine, coarse, window locked values. saved in the dictionary under "fineValue", "coarseValue", "windowLock".

// In addition, contains all other relevant values for SKAN purposes.

// e.g. 

// {

//    "skan_current_conversion_value": 3,

//    "prev_fine_value": 2,

//    "skan_first_call_to_skadnetwork_timestamp": 167890942,

//    "skan_last_call_to_skadnetwork_timestamp": 167831134,

//    "skan_total_revenue_by_currency": { "USD": 1.2 },

//    "skan_total_admon_revenue_by_currency": { "USD": 0.8 },

//    "p0_coarse": 0,

//    "p1_coarse": 1,

//    "p2_coarse": nil,

//    "p0_window_lock": 167890942,

//    "p1_window_lock": nil,

//    "p2_window_lock": nil,

//    "p0_prev_coarse_value": 0,

//    "p1_prev_coarse_value": 0,

//    "p2_prev_coarse_value": nil,

//    "p0_total_iap_revenue": nil,

//    "p1_total_iap_revenue": nil,

//    "p2_total_iap_revenue": nil,

//    "p0_total_admon_revenue": nil,

//    "p1_total_admon_revenue": nil,

//    "p2_total_admon_revenue": nil

// }

+ (NSDictionary *)getSkanDetails;

 @end

Implementación de la interfaz SKAdNetwork

El código implementa la interfaz SKAdNetwork (SKAN) de Apple para aplicaciones iOS, gestionando el seguimiento de atribuciones, valores de conversión e informes de ingresos a través de múltiples ventanas de postback.

Ejemplo de código SKANSnippet.m

Constantes y configuración

La implementación define tres ventanas de postback distintas para el seguimiento de la actividad del usuario y las conversiones.

Objective-C
static NSInteger firstSkan4WindowInSec = 3600 * 24 * 2;  // 48 hours

static NSInteger secondSkan4WindowInSec = 3600 * 24 * 7;  // 7 days

static NSInteger thirdSkan4WindowInSec = 3600 * 24 * 35; // 35 days

Características principales

  • Registro de atribución:Gestiona la configuración inicial de la atribución de la aplicación y el seguimiento del valor de conversión por primera vez.
  • Gestión del valor de conversión: Actualiza y realiza el seguimiento de los valores de conversión a través de múltiples ventanas de postback.
  • Seguimiento de ingresos: Mantiene un seguimiento separado para la monetización de anuncios y los eventos de ingresos regulares.
  • Persistencia de datos: Utiliza NSUserDefaults para almacenar los datos relacionados con SKAN en todas las sesiones de la aplicación.
  • Seguridad de hilos: Implementa NSLock para operaciones seguras durante las llamadas a la red.

Estructura de almacenamiento de datos

  • Valores de conversión de grano fino (0-63)
  • Valores gruesos (Bajo/Medio/Alto)
  • Seguimiento de ingresos por divisa
  • Gestión de marcas de tiempo para ventanas posteriores
  • Seguimiento de valores anteriores para conversiones finas y gruesas

Consideraciones de privacidad

  • Implementa funciones específicas de iOS 15.4+ y iOS 16.1
  • Gestiona las actualizaciones de valores de conversión postback de acuerdo con las directrices de privacidad de Apple
  • Mantiene un seguimiento separado de los distintos tipos de ingresos para garantizar una atribución precisa

Notas técnicas

  • Utiliza operaciones asíncronas para las llamadas de red y las actualizaciones de valores.
  • Implementa la gestión de errores y la validación de los valores de conversión.
  • Admite el seguimiento de valores de conversión tanto tradicional como grueso.
  • Gestiona múltiples ventanas de postback con diferentes duraciones y requisitos

Actualización de la integración S2S (recomendada)

Actualice su integración S2S con los siguientes metadatos SKAdNetwork.

Estos metadatos deben enviarse en cada sesión y cada evento notificado a Singular a través de Session Notification Endppint y Event Notification Endppoint. Estos datos se utilizan para la validación de la implementación de SKAdNetwork.

Estructura de los metadatos

Valores de conversión

Utilice el método Data Retrieval para extraer el diccionario de metadatos y reenviarlo a su servidor para que lo añada como parámetros de consulta en las solicitudes API de los endpoints Session y Event.

Objective-C
NSDictionary *values = [SKANSnippet getSkanDetails];

Código de implementación de SKANSnippet.m

Objective-C
//  SKANSnippet.m


#import "SKANSnippet.h"
#import <StoreKit/SKAdNetwork.h>
#import <UIKit/UIKit.h>

#define SESSION_EVENT_NAME @"__SESSION__"
#define SINGULAR_API_URL @"https://sdk-api-v1.singular.net/api/v2/conversion_value"

// SKAN Keys for NSUserDefaults persistency and requests

#define CONVERSION_VALUE_KEY @"skan_current_conversion_value"
#define FIRST_SKAN_CALL_TIMESTAMP @"skan_first_call_to_skadnetwork_timestamp"
#define LAST_SKAN_CALL_TIMESTAMP @"skan_last_call_to_skadnetwork_timestamp"
#define TOTAL_REVENUE_BY_CURRENCY @"skan_total_revenue_by_currency"
#define TOTAL_ADMON_REVENUE_BY_CURRNECY @"skan_total_admon_revenue_by_currency"
#define SKAN_UPDATED_CONVERSION_VALUE @"conversion_value"
#define SKAN_UPDATED_COARSE_VALUE @"skan_updated_coarse_value"
#define SKAN_UPDATED_LOCK_WINDOW_VALUE @"skan_updated_lock_window_value"

#define P0_COARSE @"p0_coarse"
#define P1_COARSE @"p1_coarse"
#define P2_COARSE @"p2_coarse"
#define P0_WINDOW_LOCK_TS @"p0_window_lock"
#define P1_WINDOW_LOCK_TS @"p1_window_lock"
#define P2_WINDOW_LOCK_TS @"p2_window_lock"

#define P0_PREV_FINE_VALUE @"prev_fine_value"
#define P0_PREV_COARSE_VALUE @"p0_prev_coarse_value"
#define P1_PREV_COARSE_VALUE @"p1_prev_coarse_value"
#define P2_PREV_COARSE_VALUE @"p2_prev_coarse_value"

#define TOTAL_REVENUE_P0 @"p0_total_iap_revenue"
#define TOTAL_REVENUE_P1 @"p1_total_iap_revenue"
#define TOTAL_REVENUE_P2 @"p2_total_iap_revenue"
#define TOTAL_ADMON_REVENUE_P0 @"p0_total_admon_revenue"
#define TOTAL_ADMON_REVENUE_P1 @"p1_total_admon_revenue"
#define TOTAL_ADMON_REVENUE_P2 @"p2_total_admon_revenue"

@implementation SKANSnippet

static NSInteger firstSkan4WindowInSec = 3600 * 24 * 2; //48 hours in sec
static NSInteger secondSkan4WindowInSec = 3600 * 24 * 7;
static NSInteger thirdSkan4WindowInSec = 3600 * 24 * 35;

static NSLock *lockObject;

+ (void)registerAppForAdNetworkAttribution {
    if ([SKANSnippet getFirstSkanCallTimestamp] != 0) {
        return;
    }
    
    if (@available(iOS 15.4, *)) {
        [SKAdNetwork updatePostbackConversionValue:0 completionHandler:nil];
        [SKANSnippet setFirstSkanCallTimestamp];
        [SKANSnippet setLastSkanCallTimestamp];
        [SKANSnippet valuesHasBeenUpdated:@(0) coarseValue:nil lockWindow:NO];
    } 
}

+ (void)updateConversionValuesAsync:(void(^)(NSNumber *, NSNumber *, BOOL, NSError *))handler {
    [SKANSnippet updateConversionValuesAsync:SESSION_EVENT_NAME withCompletionHandler:handler];
}

+ (void)updateConversionValuesAsync:(NSString *)eventName withCompletionHandler:(void(^)(NSNumber *, NSNumber *, BOOL, NSError *))handler {
        if ([SKANSnippet isSkanWindowOver]) {
            return;
        }
        
        [SKANSnippet getConversionValueFromServer:eventName withCompletionHandler:handler];
}

+ (void)updateRevenue:(double)amount andCurrency:(NSString *)currency isAdMonetization:(BOOL)admon {
    // Update total revenues

    if (amount == 0 || !currency ) {
        return;
    }
    
    [SKANSnippet addToTotalRevenue:@(amount) withCurrency:currency isAdmon:admon];
}

+ (NSDictionary *)getSkanDetails {
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    NSMutableDictionary *res = [NSMutableDictionary dictionary];
    //current fine
    [res setValue:[[userDefaults valueForKey:CONVERSION_VALUE_KEY] stringValue] forKey:CONVERSION_VALUE_KEY];
    //prev fine
    [res setValue:[[userDefaults valueForKey:P0_PREV_FINE_VALUE] stringValue] forKey:P0_PREV_FINE_VALUE];
    //current coarse
    [res setValue:[[userDefaults valueForKey:P0_COARSE] stringValue] forKey:P0_COARSE];
    [res setValue:[[userDefaults valueForKey:P1_COARSE] stringValue] forKey:P1_COARSE];
    [res setValue:[[userDefaults valueForKey:P2_COARSE] stringValue] forKey:P2_COARSE];
    //prev coarse
    [res setValue:[[userDefaults valueForKey:P0_PREV_COARSE_VALUE] stringValue] forKey:P0_PREV_COARSE_VALUE];
    [res setValue:[[userDefaults valueForKey:P1_PREV_COARSE_VALUE] stringValue] forKey:P1_PREV_COARSE_VALUE];
    [res setValue:[[userDefaults valueForKey:P2_PREV_COARSE_VALUE] stringValue] forKey:P2_PREV_COARSE_VALUE];
    //lock windows ts
    [res setValue:[[userDefaults valueForKey:P0_WINDOW_LOCK_TS] stringValue] forKey:P0_WINDOW_LOCK_TS];
    [res setValue:[[userDefaults valueForKey:P1_WINDOW_LOCK_TS] stringValue] forKey:P1_WINDOW_LOCK_TS];
    [res setValue:[[userDefaults valueForKey:P2_WINDOW_LOCK_TS] stringValue] forKey:P2_WINDOW_LOCK_TS];
    //total revenues
    [res setValue:[userDefaults valueForKey:TOTAL_REVENUE_BY_CURRENCY] forKey:TOTAL_REVENUE_BY_CURRENCY];
    [res setValue:[userDefaults valueForKey:TOTAL_ADMON_REVENUE_BY_CURRNECY] forKey:TOTAL_ADMON_REVENUE_BY_CURRNECY];
    //revenue per window
    [res setValue:[userDefaults valueForKey:TOTAL_REVENUE_P0] forKey:TOTAL_REVENUE_P0];
    [res setValue:[userDefaults valueForKey:TOTAL_REVENUE_P1] forKey:TOTAL_REVENUE_P1];
    [res setValue:[userDefaults valueForKey:TOTAL_REVENUE_P2] forKey:TOTAL_REVENUE_P2];
    [res setValue:[userDefaults valueForKey:TOTAL_ADMON_REVENUE_P0] forKey:TOTAL_ADMON_REVENUE_P0];
    [res setValue:[userDefaults valueForKey:TOTAL_ADMON_REVENUE_P1] forKey:TOTAL_ADMON_REVENUE_P1];
    [res setValue:[userDefaults valueForKey:TOTAL_ADMON_REVENUE_P2] forKey:TOTAL_ADMON_REVENUE_P2];
    //skan TS
    [res setValue:[[userDefaults valueForKey:LAST_SKAN_CALL_TIMESTAMP] stringValue] forKey:LAST_SKAN_CALL_TIMESTAMP];
    [res setValue:[[userDefaults valueForKey:FIRST_SKAN_CALL_TIMESTAMP] stringValue] forKey:FIRST_SKAN_CALL_TIMESTAMP];
    
    return res;
}


#pragma mark - internal
+ (BOOL)validateValues:(NSNumber *)conversionValue coarse:(NSNumber *)coarseValue{
    if ([conversionValue intValue] < 0 || 63 < [conversionValue intValue]) {
        return NO;
    }
    
    if (coarseValue) {
        if ([coarseValue intValue] > 2 || [coarseValue intValue] < 0) {
            return NO;
        }
    }
    
    return YES;
}

+ (NSURLComponents *)prepareQueryParams:(NSString *)bundleIdentifier eventName:(NSString *)eventName {
    NSURLComponents *components = [NSURLComponents componentsWithString:SINGULAR_API_URL];
    
    NSString *API_KEY = @"YOUR API KEY";
    NSString *APP_VERSION = @"YOUR APP VERSION";
    NSString *IDFV = @"IDFV";
    NSString *IDFA = @"IDFA";
    
    NSMutableArray *queryItems = [@[
        [NSURLQueryItem queryItemWithName:@"a" value:API_KEY],
        [NSURLQueryItem queryItemWithName:@"v" value:[[UIDevice currentDevice] systemVersion]],
        [NSURLQueryItem queryItemWithName:@"i" value:bundleIdentifier],
        [NSURLQueryItem queryItemWithName:@"app_v" value:APP_VERSION],
        [NSURLQueryItem queryItemWithName:@"n" value:eventName],
        [NSURLQueryItem queryItemWithName:@"p" value:@"iOS"],
        [NSURLQueryItem queryItemWithName:@"idfv" value:IDFV],
        [NSURLQueryItem queryItemWithName:@"idfa" value:IDFA]
    ] mutableCopy];
    
    NSDictionary *skanValues = [SKANSnippet getSkanDetails];
    [skanValues enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
        if ([obj isKindOfClass:[NSDictionary class]]) {
            [queryItems addObject:[NSURLQueryItem queryItemWithName:key value:[SKANSnippet dictionaryToJsonString:obj]]];
        } else {
            [queryItems addObject:[NSURLQueryItem queryItemWithName:key value:obj]];
        }
    }];
    
    components.queryItems = queryItems;
    
    return components;
}

+ (void)getConversionValueFromServer:(NSString*)eventName withCompletionHandler:(void(^)(NSNumber *, NSNumber *, BOOL, NSError*))handler {
    if (!lockObject) {
        lockObject = [NSLock new];
    }
    
    @try {
        // Making the lock async so it will not freeze the calling thread

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
            
            [lockObject lock];
            NSString *bundleIdentifier = @"YOUR BUNDLE IDENTIFIER";
            NSURLComponents *components = [SKANSnippet prepareQueryParams:bundleIdentifier eventName:eventName];
            
            [[[NSURLSession sharedSession] dataTaskWithURL:components.URL
                                         completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
                if (error) {
                    [lockObject unlock];
                    if (handler) {
                        handler(nil, nil, NO, error);
                    }
                    
                    return;
                }
                
                NSDictionary *parsedResponse = [SKANSnippet jsonDataToDictionary:data];
                
                if (!parsedResponse) {
                    [lockObject unlock];
                    if (handler) {
                        handler(nil,nil, NO, [NSError errorWithDomain:bundleIdentifier
                                                                 code:0
                                                             userInfo:@{NSLocalizedDescriptionKey:@"Failed parsing server response"}]);
                    }
                    
                    return;
                }
                
                NSNumber *conversionValue = [parsedResponse objectForKey:SKAN_UPDATED_CONVERSION_VALUE];
                NSNumber *coarseValue = [parsedResponse objectForKey:SKAN_UPDATED_COARSE_VALUE];
                BOOL lockWindow = [[parsedResponse objectForKey:SKAN_UPDATED_LOCK_WINDOW_VALUE] boolValue];
                
                
                if (!conversionValue) {
                    [lockObject unlock];
                    NSString *status = [parsedResponse objectForKey:@"status"];
                    
                    if (!status || ![status isEqualToString:@"ok"]) {
                        if (handler) {
                            NSString *reason = [parsedResponse objectForKey:@"reason"];
                            if (!reason) {
                                reason = @"Got error from server";
                            }
                            
                            handler(nil, nil, NO, [NSError errorWithDomain:bundleIdentifier
                                                                      code:0
                                                                  userInfo:@{NSLocalizedDescriptionKey:reason}]);
                        }
                    }
                    
                    return;
                }
                
                if(![SKANSnippet validateValues:conversionValue coarse:coarseValue]) {
                    if (handler) {
                        handler(nil,nil, NO, [NSError errorWithDomain:bundleIdentifier
                                                                 code:0
                                                             userInfo:@{NSLocalizedDescriptionKey:@"Illegal values recieved"}]);
                    }
                    
                    return;
                }
                
                if (![SKANSnippet getFirstSkanCallTimestamp]) {
                    [SKANSnippet setFirstSkanCallTimestamp];
                }
                
                [SKANSnippet setConversionValues:conversionValue coarseValue:coarseValue lockWindow:lockWindow handler:handler];
                
                [lockObject unlock];
            }] resume];
        });
    } @catch (id exception) {
        NSLog(@"%@", exception);
    }
}

+ (void)setFirstSkanCallTimestamp {
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    [userDefaults setInteger:[SKANSnippet getCurrentUnixTimestamp] forKey:FIRST_SKAN_CALL_TIMESTAMP];
    [userDefaults synchronize];
}

+ (void)setLastSkanCallTimestamp {
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    [userDefaults setInteger:[SKANSnippet getCurrentUnixTimestamp] forKey:LAST_SKAN_CALL_TIMESTAMP];
    [userDefaults synchronize];
}

+ (NSString*)dictionaryToJsonString:(NSDictionary*)dictionary {
    if (!dictionary || [dictionary count] == 0){
        return @"{}";
    }
    
    NSError *error;
    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dictionary
                                                       options:0
                                                         error:&error];
    
    if (error || !jsonData) {
        return @"{}";
    }
    
    return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}

+ (NSInteger)getFirstSkanCallTimestamp {
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    return [userDefaults integerForKey:FIRST_SKAN_CALL_TIMESTAMP];
}

+ (NSInteger)getLastSkanCallTimestamp {
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    return [userDefaults integerForKey:LAST_SKAN_CALL_TIMESTAMP];
}

+ (NSInteger)getCurrentUnixTimestamp {
    return [[NSDate date] timeIntervalSince1970];
}

+ (void)setConversionValues:(NSNumber *)conversionValue coarseValue:(NSNumber *)coarse lockWindow:(BOOL)lockWindow handler:(void(^)(NSNumber *, NSNumber *, BOOL, NSError*))handler {
    @try {
        __block void(^skanResultHandler)(NSError * _Nullable error) = ^(NSError * _Nullable error) {
            if (handler) {
                if (error) {
                    handler(nil, nil, NO, error);
                } else {
                    handler(conversionValue, coarse, lockWindow, nil);
                }
            }
            
            [SKANSnippet valuesHasBeenUpdated:conversionValue coarseValue:coarse lockWindow:lockWindow];
        };
        
        if (@available(iOS 16.1, *)) {
            [SKAdNetwork updatePostbackConversionValue:[conversionValue integerValue] coarseValue:[SKANSnippet resolveCoarseValueFrom:coarse] lockWindow:lockWindow completionHandler:^(NSError * _Nullable error) {
                skanResultHandler(error);
            }];
        } else {
            if (@available(iOS 15.4, *)) {
                [SKAdNetwork updatePostbackConversionValue:[conversionValue integerValue] completionHandler:^(NSError * _Nullable error) {
                    skanResultHandler(error);
                }];
            }
        }
    } @catch (id exception) {
        NSLog(@"%@", exception);
    }
}

+ (NSNumber *)getConversionValue {
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    
    if (![userDefaults objectForKey:CONVERSION_VALUE_KEY]) {
        return @(0);
    }
    
    return @([userDefaults integerForKey:CONVERSION_VALUE_KEY]);
}

+ (NSDictionary*)jsonDataToDictionary:(NSData*)jsonData {
    if (!jsonData) {
        return nil;
    }
    
    NSError *error;
    NSDictionary *parsedData = [NSJSONSerialization JSONObjectWithData:jsonData
                                                                options:kNilOptions error:&error];
    
    if (error || !parsedData) {
        return nil;
    }
    
    return parsedData;
}

+ (NSInteger)getCurrentSkanWindow {
    NSInteger timeDiff = [SKANSnippet getCurrentUnixTimestamp] - [SKANSnippet getFirstSkanCallTimestamp];
    if (timeDiff < firstSkan4WindowInSec) { return 0; }
    if (timeDiff < secondSkan4WindowInSec) { return 1; }
    if (timeDiff < thirdSkan4WindowInSec) { return 2; }
    
    return -1;
}

// persist updated conversion values based on the active skan window.

+ (void)valuesHasBeenUpdated:(NSNumber *)fineValue coarseValue:(NSNumber *)coarseValue lockWindow:(BOOL)lockWindow {
    NSNumber *currentPersistedFineValue;
    NSNumber *currentPersistedCoarseValue;
    NSInteger window = [SKANSnippet getCurrentSkanWindow];
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
        
    switch (window) {
    case 0:
        currentPersistedFineValue = [userDefaults objectForKey:CONVERSION_VALUE_KEY];
        currentPersistedCoarseValue = [userDefaults objectForKey:P0_COARSE];
        [userDefaults setValue:fineValue forKey:CONVERSION_VALUE_KEY];
        [userDefaults setValue:currentPersistedFineValue forKey:P0_PREV_FINE_VALUE];
        [userDefaults setValue:coarseValue forKey:P0_COARSE];
        [userDefaults setValue:currentPersistedCoarseValue forKey:P0_PREV_COARSE_VALUE];
            
        if (lockWindow) {
            [userDefaults setObject:@([SKANSnippet getCurrentUnixTimestamp]) forKey:P0_WINDOW_LOCK_TS];
        }
            
        break;
    
    case 1:
        currentPersistedCoarseValue = [userDefaults objectForKey:P1_COARSE];
        [userDefaults setValue:coarseValue forKey:P1_COARSE];
        [userDefaults setValue:currentPersistedCoarseValue forKey:P1_PREV_COARSE_VALUE];
            
        if (lockWindow) {
            [userDefaults setObject:@([SKANSnippet getCurrentUnixTimestamp]) forKey:P1_WINDOW_LOCK_TS];
        }
            
        break;
    
    case 2:
        currentPersistedCoarseValue = [userDefaults objectForKey:P2_COARSE];
        [userDefaults setValue:coarseValue forKey:P2_COARSE];
        [userDefaults setValue:currentPersistedCoarseValue forKey:P2_PREV_COARSE_VALUE];
            
        if (lockWindow) {
            [userDefaults setValue:@([SKANSnippet getCurrentUnixTimestamp]) forKey:P2_WINDOW_LOCK_TS];
        }
            
        break;
    }
    
    [SKANSnippet setLastSkanCallTimestamp];
}

+ (BOOL)isSkanWindowOver {
    NSInteger timeDiff = [SKANSnippet getCurrentUnixTimestamp] - [SKANSnippet getFirstSkanCallTimestamp];
    return thirdSkan4WindowInSec <= timeDiff;
}

// Revenues are being accumulated and saved by Ad monetization and non ad monetization events, total sum and break down by skan windows.

+ (void)addToTotalRevenue:(NSNumber *)newRevenue withCurrency:(NSString *)currency isAdmon:(BOOL)isAdmon  {
    NSString *key = isAdmon ? TOTAL_ADMON_REVENUE_BY_CURRNECY : TOTAL_REVENUE_BY_CURRENCY;
    [SKANSnippet addToTotalRevenue:newRevenue withCurrency:currency forKey:key];
        
    NSInteger window = [SKANSnippet getCurrentSkanWindow];
    switch (window) {
        case 0:
            key = isAdmon ? TOTAL_ADMON_REVENUE_P0 : TOTAL_REVENUE_P0 ;
            break;
        case 1:
            key = isAdmon ? TOTAL_ADMON_REVENUE_P1 : TOTAL_REVENUE_P1 ;
            break;
            
        case 2:
            key = isAdmon ? TOTAL_ADMON_REVENUE_P2 : TOTAL_REVENUE_P2 ;
            break;
        case -1:
            key = nil;
            return;
    }
    
    [SKANSnippet addToTotalRevenue:newRevenue withCurrency:currency forKey:key];
}

// Coarse value is being sent on requests and responses as an Int and being translated into the system defined coarse value upon API execution.

+ (NSString *)resolveCoarseValueFrom:(NSNumber *)value {
    if(@available(iOS 16.1, *)) {
        if (!value) {
            return nil;
        }
        
        switch ([value integerValue]) {
            case 0:
                return SKAdNetworkCoarseConversionValueLow;
            case 1:
                return SKAdNetworkCoarseConversionValueMedium;
            case 2:
                return SKAdNetworkCoarseConversionValueHigh;
            default:
                return nil;
        }
    }
    
    return nil;
}

+ (NSDictionary*)getTotalRevenue:(NSString *)revenueKey {
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    
    if (![userDefaults objectForKey:revenueKey]){
        [userDefaults setObject:[NSDictionary dictionary] forKey:revenueKey];
    }
    
    return [userDefaults objectForKey:revenueKey];
}

+ (void)addToTotalRevenue:(NSNumber *)newRevenue withCurrency:(NSString *)currency forKey:(NSString *)revenueKey {
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    NSMutableDictionary *revenues = [[SKANSnippet getTotalRevenue:revenueKey] mutableCopy];
    NSNumber *currentRevenue = 0;

    if ([revenues objectForKey:currency]) {
        currentRevenue = [revenues objectForKey:currency];
    }

    currentRevenue = @([currentRevenue floatValue] + [newRevenue floatValue]);
    [revenues setObject:currentRevenue forKey:currency];
    [userDefaults setObject:revenues forKey:revenueKey];
    [userDefaults synchronize];
}

@end

Ejemplo de flujo del ciclo de vida de la aplicación

Este código demuestra los puntos clave de integración para la atribución de SKAdNetwork (SKAN) en el ciclo de vida de una aplicación iOS, gestionando lanzamientos de aplicaciones, sesiones, eventos y seguimiento de ingresos.

Notas de implementación

  • El código utiliza métodos asíncronos para las actualizaciones de los valores de conversión con el fin de evitar el bloqueo del hilo principal.
  • Todos los datos relacionados con SKAN se recopilan en un formato de diccionario antes de ser enviados al servidor.
  • La implementación sigue el enfoque de Apple de prioridad a la privacidad, al tiempo que permite un seguimiento esencial de la atribución.
  • El seguimiento de los ingresos incluye tanto el valor monetario como la especificación de la divisa para obtener informes financieros precisos.

Primer lanzamiento de la aplicación

Este código registra la aplicación en SKAdNetwork para el seguimiento de atribuciones y envía los datos de la sesión inicial al endpoint de Singular. Sólo se ejecuta en el primer lanzamiento de la aplicación para establecer el seguimiento de la atribución.

Esto sólo se ejecuta en el primer lanzamiento de la aplicación para establecer el seguimiento de la atribución.

Objective-C
[SKANSnippet registerAppForAdNetworkAttribution];
NSDictionary *skanValues = [SKANSnippet getSkanDetails];
[self sendSessionToServer:skanValues] //to Singular launch EP 

Gestión de sesiones

Esta sección actualiza los valores de conversión después de cada sesión y envía los detalles actualizados de SKAN para rastrear el compromiso del usuario.

Objective-C
[SKANSnippet updateConversionValuesAsync:handler];
NSDictionary *skanValues = [SKANSnippet getSkanDetails];
[self sendSessionToServer:skanValues]

Seguimiento de eventos

Este código gestiona los eventos que no generan ingresos actualizando los valores de conversión y enviando los datos del evento al punto final de eventos de Singular.

Objective-C
[SKANSnippet updateConversionValuesAsync:@"event_name" withCompletionHandler:handler];
NSDictionary *skanValues = [SKANSnippet getSkanDetails];
[self sendEventToServer:skanValues eventName:@"event_name"]

Seguimiento de ingresos

Esta sección gestiona los eventos de ingresos actualizando tanto el importe de los ingresos con la divisa como los valores de conversión asociados. A continuación, los datos se envían al punto final de eventos de Singular para realizar un seguimiento de las actividades relacionadas con las compras.

Objective-C
[SKANSnippet updateRevenue:15.53 andCurrency:@"KRW"];
[SKANSnippet updateConversionValuesAsync:@"revenue_event_name" withCompletionHandler:handler];
NSDictionary *skanValues = [SKANSnippet getSkanDetails];
[self sendEventToServer:skanValues eventName:@"revenue_event_name"]

API de valor de conversión

El valor de conversión de SKAdNetwork puede notificarse a través de dos métodos:

  1. Implementación directa de la interfaz SKAdNetwork en el lado del cliente (véase más arriba).
  2. Integración del lado del servidor mediante el punto final de la API de valor de conversión.

Ambos métodos mantienen el mismo flujo de datos y la integridad de los informes, lo que le permite elegir la implementación que mejor se adapte a su arquitectura técnica. El punto final de la API de valor de conversión acepta parámetros idénticos a los de la interfaz del lado del cliente, lo que garantiza un seguimiento coherente de la atribución.

Referencia de la API de valor de conversión

Contenido


Punto final de la API de valor de conversión

Método HTTP y valor de conversión Endpoint

GET https://sdk-api-v1.singular.net/api/v2/conversion_value

Parámetros obligatorios

La siguiente tabla enumera los parámetros obligatorios y opcionales para admitir la API de conversión desde su servidor. Todos los parámetros enumerados son parámetros de consulta.

Parámetros requeridos
Clave API
Parámetro Descripción
a
string

El parámetro a especifica la clave Singular SDK Key.

Obtenga la clave SDK en la interfaz de Singular, en Herramientas de desarrollo del menú principal.

Nota: No utilice la clave API de generación de informes, ya que se rechazarán los datos.

 

Valor de ejemplo:
sdkKey_afdadsf7asf56
Parámetros del identificador del dispositivo
Parámetro Descripción
idfa
string

El parámetro idfa especifica el Identificador para Anunciantes (IDFA) que ayuda a los anunciantes a rastrear y atribuir las acciones de los usuarios (por ejemplo, clics en anuncios, instalaciones de aplicaciones) a campañas específicas, lo que permite una orientación precisa de los anuncios y la optimización de las campañas.

A partir de iOS 14.5, los usuarios deben optar por el marco App Tracking Transparency (ATT) para que las aplicaciones puedan acceder al IDFA. Si los usuarios no optan por el IDFA, este no estará disponible, lo que limitará las capacidades de seguimiento.

 

Valor de ejemplo:
DFC5A647-9043-4699-B2A5-76F03A97064B
Parámetro Descripción
idfv
string

El parámetro idfv especifica el Identificador para Vendedores (IDFV), un identificador único asignado por Apple a un dispositivo, que es específico de un vendedor o desarrollador concreto. Permanece consistente en todas las aplicaciones del mismo proveedor en un dispositivo dado, permitiendo al proveedor rastrear el comportamiento del usuario y las interacciones a través de su ecosistema de aplicaciones sin identificar al usuario personalmente.

 

Valor de ejemplo:
21DB6612-09B3-4ECC-84AC-B353B0AF1334
Parámetros del dispositivo
Parámetro Descripción
p
string

El parámetro p especifica la plataforma de la aplicación. Dado que esta API solo se utiliza para iOS, este valor debe ser iOS.

 

Valor de ejemplo:
iOS
Parámetro Descripción
v
string

El parámetro v especifica la versión del sistema operativo del dispositivo en el momento de la sesión.

 

Valor de ejemplo:
16.1
Parámetros de aplicación
Parámetro Descripción
i
string

El parámetro i especifica el identificador de la aplicación.

Se trata del ID de paquete de la aplicación iOS. (distingue mayúsculas de minúsculas)

Valor de ejemplo:
com.singular.app
Parámetro Descripción
app_v
string

El parámetro app_v especifica la Versión de la Aplicación.

 

Ejemplos:
1.2.3
Parámetros de evento
Parámetro Descripción
n
string

El parámetro n especifica el Nombre del evento que se está rastreando.

  • Limitación: máximo 32 caracteres ASCII
  • Para las sesiones, utilice el nombre del evento:
    __SESSION__
  • Para los eventos que no sean de sesión, utilice el mismo nombre de evento y el mismo encabezamiento enviados a Singular a través del punto final de la API de eventos.

 

Valor de ejemplo:
sng_add_to_cart
Parámetros de valor de conversión
Parámetro Descripción
skan_current_conversion_value

Plataformas compatibles:

  • iOS 15.4+
int

El último valor de conversión de SKAdNetwork, en el momento de la notificación de la sesión/evento anterior. Se trata de un número entero comprendido entre (0-63).

 

Valor de ejemplo:

7
Parámetro Descripción
p1_coarse

Plataformas compatibles:

  • iOS 16.1+
int

El último valor de conversión gruesa de SKAdNetwork para postback_sequence 1, en el momento de la notificación de la sesión/evento anterior. Se trata de un número entero comprendido entre (0-2).

 

Valor de ejemplo:

0
Parámetro Descripción
p2_coarse

Plataformas compatibles:

  • iOS 16.1+
int

El último valor de conversión gruesa de SKAdNetwork para postback_sequence 2, en el momento de la notificación de la sesión/evento anterior. Se trata de un número entero comprendido entre (0-2).

 

Valor de ejemplo:

1
Parámetros de seguimiento de ingresos
Parámetro Descripción
skan_total_revenue_by_currency

Plataformas compatibles:

  • iOS 15.4+
JSON URL-encoded string

Obligatorio si se utilizan los modelos IAP o Todos los ingresos. Total agregado actual de ingresos IAP generados por el dispositivo, excluidos los ingresos por monetización de anuncios.

{
   "USD":9.99
}

 

Valor de ejemplo:

%7B%22USD%22%3A9.99%7D
Parámetro Descripción
skan_total_admon_revenue_by_currency

Plataformas compatibles:

  • iOS 15.4+
JSON URL-encoded string

Obligatorio si se utilizan los modelos de conversión Admon o Todos los ingresos. Total agregado actual de ingresos por monetización de anuncios generados por el dispositivo.

{
   "USD":1.2
}

 

Valor de ejemplo:

%7B%22USD%22%3A5%7D
Timestamp Parámetros
Parámetro Descripción
skan_first_call_to_skadnetwork_timestamp

Plataformas compatibles:

  • iOS 15.4+
int

Marca de tiempo Unix de la primera llamada a la API SKAdNetwork subyacente.

 

Valor de ejemplo:

1483228800
Parámetro Descripción
skan_last_call_to_skadnetwork_timestamp

Plataformas soportadas:

  • iOS 15.4+
int

Marca de tiempo Unix de la última llamada a la API SKAdNetwork subyacente, en el momento de esta notificación de sesión.

 

Valor de ejemplo:

1483228800

Cuerpo de la solicitud

No proporcione un cuerpo de solicitud al llamar a este método. La solicitud debe enviarse utilizando el método GET con parámetros de consulta.

 

Ejemplos de solicitud

Es posible que los siguientes ejemplos de código no representen todos los parámetros admitidos. Cuando implemente la petición asegúrese de incluir todos los parámetros requeridos como se indica arriba, y valide que se están pasando los valores correctos antes de enviar datos desde una instancia de producción. Se recomienda utilizar un único parámetro `i` (identificador de aplicación) para el desarrollo y las pruebas.

 

PYTHON CURL HTTP JAVA

PYTHON

import requests

params = {
    'a': 'sdk_key_here',
    'p': 'iOS',
    'i': 'com.singular.app',
    'v': '16.1',
    'idfa': 'DFC5A647-9043-4699-B2A5-76F03A97064B',
    'idfv': '21DB6612-09B3-4ECC-84AC-B353B0AF1334',
    'n': '__SESSION__',
    'app_v': '1.2.3',
    'skan_current_conversion_value': 7,
    'p1_coarse': 0,
    'p2_coarse': 1,
    'skan_total_revenue_by_currency': {"USD":9.99},
    'skan_total_admon_revenue_by_currency': {"USD":1.2},
    'skan_first_call_to_skadnetwork_timestamp': 1510090877,
    'skan_last_call_to_skadnetwork_timestamp': 1510090877
}

response = requests.get('https://sdk-api-v1.singular.net/api/v2/conversion_value', params=params)
print(response.json())

 

Solicitud de respuesta

A continuación se muestra una respuesta API correcta con un nuevo valor de conversión devuelto.

Respuesta HTTP
200 - ok

El 200 - ok sin ningún error o razón en el cuerpo de la respuesta significa que la solicitud fue enviada a la cola para su procesamiento.

 

Respuesta:
{
   "conversion_value":1,
   "skan_updated_coarse_value":0,
   "postback_sequence_index":0,
   "status":"ok"
}

Parámetros de respuesta

La siguiente tabla define los parámetros de respuesta.

Clave Descripción Ejemplo Valor
conversion_value

Nuevo valor de conversión fina

0-63
skan_updated_coarse_value

Nuevo valor de conversión gruesa

0-2
postback_sequence_index

Corresponde a los periodos de medición postback de SKAN:

  • 0 = postback 1
  • 1 = postback 2
  • 2 = postback 3

Indica qué clave de valor de conversión gruesa debe actualizarse: es decir, p0_grueso, p1_grueso, p2_grueso

0-2
status

Ok para procesado con éxito

ok

Posibles errores de respuesta

  • Han pasado más de 24 horas desde la última actualización de la conversión (28032 horas), la ventana de actualización está cerrada:
    • Horas calculadas como:
      (skan_last_call_to_skadnetwork_timestamp) - (skan_first_call_to_skadnetwork_timestamp)
  • Error de plataforma desconocido - Plataforma no iOS
  • Gestión de conversión: Parámetro %x no válido
  • Gestión de conversión: Modelo de conversión no encontrado para app: %x
  • Periodo de medición no válido: %x
  • Gestión de conversiones: No se encuentra la moneda del propietario: %customer

Parámetros opcionales

La siguiente tabla enumera los parámetros opcionales utilizados para soportar la versión 4 de SKAdNetwork. Todos los parámetros enumerados son parámetros de consulta.

Parámetros opcionales
Parámetros de valor de conversión
Parámetro Descripción
p0_coarse

Plataformas compatibles:

  • iOS 16.1+
int

No es necesario. Esta clave se asigna a partir de la clave skan_current_conversion_value. En otras palabras, el modelo no utiliza p0_coarse al evaluar el skan_updated_coarse_value que se devolverá en la respuesta, sino que utiliza el skan_current_conversion_value. Se trata de un número entero comprendido entre (0-2).

 

Valor de ejemplo:

0
Parámetro Descripción
p0_prev_coarse_value

Plataformas compatibles:

  • iOS 16.1+
int

El valor grueso anterior de p0. Se trata de un número entero comprendido entre (0-2).

 

Valor de ejemplo:

0
Parámetro Descripción
p1_prev_coarse_value

Plataformas compatibles:

  • iOS 16.1+
int

El valor grueso anterior de p1. Se trata de un número entero comprendido entre (0-2).

 

Valor de ejemplo:

0
p2_prev_coarse_value

Plataformas compatibles:

  • iOS 16.1+
int

El valor grueso anterior de p2. Es un número entero comprendido entre (0-2).

 

Valor de ejemplo:

0
Seguimiento de ingresos
p0_total_iap_revenue

Plataformas compatibles:

  • iOS 16.1+
JSON URL-encoded string

Total de ingresos IAP para p0, excluidos los ingresos por monetización de anuncios.

{
   "USD":9.99
}

 

Valor de ejemplo:

%7B%22USD%22%3A9.99%7D
p1_total_iap_revenue

Plataformas compatibles:

  • iOS 16.1+
JSON URL-encoded string

Total de ingresos IAP para p1, excluidos los ingresos por monetización de anuncios.

{
   "USD":9.99
}

 

Valor de ejemplo:

%7B%22USD%22%3A9.99%7D
p2_total_iap_revenue

Plataformas soportadas:

  • iOS 16.1+
JSON URL-encoded string

Total de ingresos IAP para p2, excluidos los ingresos por monetización de anuncios.

{
   "USD":9.99
}

 

Valor de ejemplo:

%7B%22USD%22%3A9.99%7D
p0_total_admon_revenue

Plataformas soportadas:

  • iOS 16.1+
JSON URL-encoded string

Total de ingresos por monetización de anuncios para p0.

{
   "USD":1.2
}

 

Valor de ejemplo:

%7B%22USD%22%3A1.2%7D
p1_total_admon_revenue

Plataformas soportadas:

  • iOS 16.1+
JSON URL-encoded string

Total de ingresos por monetización de anuncios para p1.

{
   "USD":1.2
}

 

Valor de ejemplo:

%7B%22USD%22%3A1.2%7D
p2_total_admon_revenue

Plataformas soportadas:

  • iOS 16.1+
JSON URL-encoded string

Total de ingresos por monetización de anuncios para p2.

{
   "USD":1.2
}

 

Valor de ejemplo:

%7B%22USD%22%3A1.2%7D
Timestamp Parámetros
Parámetro Descripción
p0_window_lock

Plataformas compatibles:

  • iOS 16.1+
int

Marca de tiempo Unix de la última actualización con bloqueo de ventana para p0

Nota: el modelo de conversión singular no tiene en cuenta actualmente el bloqueo de ventanas, pero es posible que lo haga con el tiempo.

 

Valor de ejemplo:

1483228850
Parámetro Descripción
p1_window_lock

Plataformas compatibles:

  • iOS 16.1+
int

Marca de tiempo Unix de la última actualización con bloqueo de ventana para p1

Nota: el modelo de conversión singular no tiene en cuenta actualmente el bloqueo de ventanas, pero es posible que lo haga con el tiempo.

 

Valor de ejemplo:

1483228850
Parámetro Descripción
p2_window_lock

Plataformas compatibles:

  • iOS 16.1+
int

Marca de tiempo Unix de la última actualización con bloqueo de ventana para p2

Nota: el modelo de conversión singular no tiene en cuenta actualmente el bloqueo de ventanas, pero es posible que lo haga con el tiempo.

 

Valor de ejemplo:

1483228850