Guía de implementación S2S de SKAdNetwork 3.0 (Legado)

Legado — SKAdNetwork 3. Esta guía cubre la implementación legada de SKAdNetwork 3.0 y se conserva únicamente para integraciones existentes. Para todas las implementaciones nuevas y actuales, utilice la Guía de implementación de SKAdNetwork 4 .

Guía de implementación S2S de SKAdNetwork 3.0 (Legado)

Implemente el framework SKAdNetwork 3.0 de Apple para la atribución en iOS que respeta la privacidad, utilizando una integración server-to-server para medir el rendimiento de las campañas de instalación de apps sin comprometer los identificadores de usuario.


Descripción general

¿Qué es SKAdNetwork?

SKAdNetwork es el framework de atribución de Apple que preserva la privacidad y permite medir las tasas de conversión de las campañas publicitarias de instalación de apps en iOS sin requerir identificadores a nivel de usuario.

El framework procesa la atribución a través de los servidores de la App Store, desvinculando la información de atribución de los identificadores de usuario y de los datos temporales antes de enviarla a las redes publicitarias.

Documentación de Apple: SKAdNetwork Framework Reference


Cómo funciona SKAdNetwork

El proceso de atribución ocurre en su totalidad a través de la infraestructura de Apple, garantizando la privacidad del usuario y permitiendo a la vez medir el rendimiento de las campañas.

Flujo de atribución de SKAdNetwork

Flujo de atribución:

  1. Clic en el anuncio: El usuario hace clic en un anuncio que contiene la firma de SKAdNetwork con la red, el publisher y el ID de campaña
  2. Apertura de la App Store: El dispositivo almacena los datos de atribución y abre la App Store para la instalación
  3. Lanzamiento de la app: El usuario instala e inicia la app por primera vez
  4. Registro: La app se registra con el framework SKAdNetwork para señalar una instalación exitosa
  5. Conversion Value: La app actualiza opcionalmente el conversion value (0-63) que representa la actividad posterior a la instalación
  6. Ventana del temporizador: El dispositivo espera más de 24 horas después del primer lanzamiento o de la última actualización del conversion value
  7. Postback: La App Store envía el postback de atribución a la red publicitaria con el ID de campaña, el conversion value y una firma criptográfica

Privacidad por diseño:

  • El postback no contiene identificadores de dispositivo ni de usuario
  • El retraso mínimo de 24 horas impide la correlación temporal
  • La app nunca sabe en qué anuncio hizo clic el usuario
  • La red nunca sabe qué usuario específico realizó la instalación

Capacidades y limitaciones

SKAdNetwork ofrece:

  • Atribución de último clic: Funciona sin el consentimiento del usuario ni la aceptación de ATT
  • Desglose de campañas: Granularidad por fuente, campaña y publisher
  • Conversion values: 64 valores discretos (0-63) para la medición posterior a la instalación
  • Prevención de fraude: Las firmas criptográficas validan la autenticidad de la atribución

Limitaciones actuales:

  • Sin datos a nivel de usuario: No puede rastrear los recorridos individuales de los usuarios
  • Sin view-through: Solo atribución basada en clics
  • Conversion values limitados: Un único valor de 6 bits (0-63) por instalación
  • Granularidad limitada: Máximo 100 IDs de campaña, sin desglose por grupo de anuncios ni por creativo
  • Sin cohortes largas: Marco temporal limitado para la medición de conversiones
  • Exposición al fraude: El conversion value en sí no está firmado, los postbacks pueden duplicarse

Recursos de Singular

Singular ofrece recursos completos para la implementación y la optimización de SKAdNetwork.


Solución de SKAdNetwork de Singular

La solución de SKAdNetwork de Singular ofrece una gestión de atribución de extremo a extremo, desde la implementación del lado del cliente hasta el procesamiento de postbacks y la optimización de campañas.

Componentes de la solución

Funciones de la plataforma

Soporte completo de SKAdNetwork en todo el flujo de trabajo de atribución y analítica.

Componente Funcionalidad
Código del lado del cliente Ejemplos de código iOS nativo para el registro con el framework SKAdNetwork y la gestión de conversion values
Procesamiento de postbacks Validación y agregación de postbacks de todas las redes publicitarias con reportes unificados
Protección contra fraude Validación de firma criptográfica, deduplicación de transaction ID y verificación segura de parámetros
Gestión de conversiones Configuración dinámica en el dashboard para codificar las actividades posteriores a la instalación en conversion values
Reportes Traducción del ID de campaña y enriquecimiento con parámetros de marketing para un análisis granular
Postbacks a partners Conversion values decodificados enviados como eventos e ingresos para la optimización del partner

Arquitectura de la integración S2S

Componentes de la implementación

La integración server-to-server de SKAdNetwork consta de dos áreas principales de implementación.

1. Implementación del lado del cliente (obligatoria):

  • Registro con el framework SKAdNetwork al iniciar la app
  • Gestión inteligente de conversion values basada en la actividad posterior a la instalación
  • Esencial para la optimización de campañas mediante la atribución de SKAdNetwork

2. Actualización de la integración S2S (opcional):

  • Enriquece los eventos y las sesiones S2S de Singular con metadatos de SKAdNetwork
  • Permite la validación de la implementación y la resolución de problemas
  • Proporciona marcas de tiempo del conversion value para la depuración

Implementación del lado del cliente

Implemente el registro con el framework SKAdNetwork y la gestión de conversion values utilizando los ejemplos de código iOS nativo de Singular para una medición óptima de campañas.

Responsabilidades de la implementación

Funcionalidad principal

El código del lado del cliente gestiona dos funciones críticas para la medición de SKAdNetwork.

  1. Registro con SKAdNetwork: Registra la app con el framework inmediatamente después del lanzamiento para habilitar la atribución
  2. Gestión de conversion values:
    • Se comunica de forma síncrona con el endpoint de Singular para recibir el próximo conversion value
    • Reporta sesiones, eventos e ingresos a Singular
    • Recibe un conversion value codificado que representa la actividad posterior a la instalación configurada
    • Recopila metadatos de SKAdNetwork (marca de tiempo de la primera llamada, marca de tiempo de la última llamada, valor actual)

Interfaz de SKAdNetwork

Definiciones de métodos

La interfaz incluye métodos para el registro con el framework, el seguimiento de sesiones, el seguimiento de eventos y la medición de ingresos.

SKANSnippet.h
//  SKANSnippet.h

#import <Foundation/Foundation.h>

@interface SKANSnippet : NSObject

// Register for SKAdNetwork attribution.
// Call this method as soon as possible once app is launched
+ (void)registerAppForAdNetworkAttribution;

// Track retention and cohorts by calling after each session.
// Reports session details and updates conversion value if needed.
// Conversion value updates only if new value greater than previous.
// Optional callback runs once conversion value updated.
+ (void)updateConversionValueAsync:(void(^)(int, NSError*))handler;

// Track conversion events by calling after each event.
// Reports event details and updates conversion value if needed.
// Optional callback runs once conversion value updated.
+ (void)updateConversionValueAsync:(NSString*)eventName
               withCompletionHandler:(void(^)(int, NSError*))handler;

// Track revenue by calling before each revenue event.
// Updates total revenue for next conversion value calculation.
// Note:
// 1. Call before 'updateConversionValueAsync' to ensure revenue included
// 2. Avoid calling on event retries to prevent double-counting
+ (void)updateRevenue:(double)amount andCurrency:(NSString*)currency;

// Gets current conversion value (nil if none set)
+ (NSNumber *)getConversionValue;

@end

Método de registro

registerAppForAdNetworkAttribution registra la app con el framework SKAdNetwork, iniciando el temporizador de atribución de 24 horas.

Comportamiento del temporizador:

  • El dispositivo envía la notificación de instalación entre 0 y 24 horas después de que expira el temporizador
  • Actualizar el conversion value con un valor mayor reinicia el temporizador para un nuevo intervalo de 24 horas
  • Referencia de Apple: updateConversionValue Documentation

Métodos de conversion value

Los métodos calculan y actualizan los conversion values según la actividad posterior a la instalación y el modelo de conversión configurado.

Actividades admitidas:

  • Sesiones: Críticas para la medición de retención
  • Eventos de conversión: Críticos para la medición de eventos posteriores a la instalación
  • Eventos de ingresos: Críticos para la medición de ingresos

Implementación de la interfaz

Código completo de implementación

La implementación completa gestiona el registro, la gestión de conversiones y la comunicación con la API de Singular.

SKANSnippet.m
//  SKANSnippet.m

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

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

// Keys for UserDefaults storage
#define CONVERSION_VALUE_KEY @"skan_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_KEY @"skan_total_revenue_by_currency"
#define SECONDS_PER_DAY 86400

static NSLock *lockObject;

@implementation SKANSnippet

+ (void)registerAppForAdNetworkAttribution {
    if ([SKANSnippet getFirstSkanCallTimestamp] != 0) {
        return;
    }

    if (@available(iOS 11.3, *)) {
        [SKAdNetwork registerAppForAdNetworkAttribution];
        [SKANSnippet setFirstSkanCallTimestamp];
        [SKANSnippet setLastSkanCallTimestamp];
    }
}

+ (void)updateConversionValueAsync:(void(^)(int, NSError*))handler {
    [SKANSnippet updateConversionValueAsync:SESSION_EVENT_NAME withCompletionHandler:handler];
}

+ (void)updateConversionValueAsync:(NSString*)eventName withCompletionHandler:(void(^)(int, NSError*))handler {
    if (@available(iOS 14, *)) {
        if ([SKANSnippet isSkanUpdateWindowOver]) {
            return;
        }

        [SKANSnippet getConversionValueFromServer:eventName withCompletionHandler:handler];
    }
}

+ (void)updateRevenue:(double)amount andCurrency:(NSString*)currency {
    if (@available(iOS 14, *)) {

        // Update total revenues
        if (amount != 0 && currency) {
            NSMutableDictionary* revenues = [[SKANSnippet getTotalRevenue] mutableCopy];
            NSNumber* currentRevenue = @(0);

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

            currentRevenue = @([currentRevenue floatValue] + amount);
            [revenues setObject:currentRevenue forKey:currency];

            [SKANSnippet setTotalRevenue:revenues];
        }
    }
}

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

+ (void)getConversionValueFromServer:(NSString*)eventName withCompletionHandler:(void(^)(int, NSError*))handler {
    if (!lockObject) {
        lockObject = [NSLock new];
    }

    // 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];

        NSURLComponents *components = [NSURLComponents componentsWithString:SINGULAR_API_URL];

        NSString* bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];

        components.queryItems = @[
            [NSURLQueryItem queryItemWithName:@"a" value:@"YOUR_SDK_KEY"],
            [NSURLQueryItem queryItemWithName:@"i" value:bundleIdentifier],
            [NSURLQueryItem queryItemWithName:@"app_v" value:@"YOUR_APP_VERSION"],
            [NSURLQueryItem queryItemWithName:@"n" value:eventName],
            [NSURLQueryItem queryItemWithName:@"p" value:@"iOS"],
            [NSURLQueryItem queryItemWithName:@"idfv" value:@"YOUR_IDFV"],
            [NSURLQueryItem queryItemWithName:@"idfa" value:@"YOUR_IDFA"],
            [NSURLQueryItem queryItemWithName:@"conversion_value"  value:[[SKANSnippet getConversionValue] stringValue]],
            [NSURLQueryItem queryItemWithName:@"total_revenue_by_currency"  value:[SKANSnippet dictionaryToJsonString:[SKANSnippet getTotalRevenue]]],
            [NSURLQueryItem queryItemWithName:@"first_call_to_skadnetwork_timestamp"  value:[NSString stringWithFormat:@"%ld", [SKANSnippet getFirstSkanCallTimestamp]]],
            [NSURLQueryItem queryItemWithName:@"last_call_to_skadnetwork_timestamp"  value:[NSString stringWithFormat:@"%ld", [SKANSnippet getLastSkanCallTimestamp]]],
        ];

        [[[NSURLSession sharedSession] dataTaskWithURL:components.URL
                                     completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            if (error) {
                [lockObject unlock];
                if (handler) {
                    handler(-1, error);
                }
                return;
            }

            NSDictionary* parsedResponse = [SKANSnippet jsonDataToDictionary:data];

            if (!parsedResponse) {
                [lockObject unlock];
                if (handler) {
                    handler(-1, [NSError errorWithDomain:bundleIdentifier
                                                    code:0
                                                userInfo:@{NSLocalizedDescriptionKey:@"Failed parsing server response"}]);
                }
                return;
            }

            NSNumber *conversionValue = [parsedResponse objectForKey:@"conversion_value"];

            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(-1, [NSError errorWithDomain:bundleIdentifier
                                                        code:0
                                                    userInfo:@{NSLocalizedDescriptionKey:reason}]);
                    }
                }
                return;
            }

            NSNumber* currentValue = [SKANSnippet getConversionValue];
            if ([conversionValue intValue] <= [currentValue intValue]) {
                [lockObject unlock];
                return;
            }

            [SKANSnippet setConversionValue:[conversionValue intValue]];
            [SKANSnippet setLastSkanCallTimestamp];

            if (![SKANSnippet getFirstSkanCallTimestamp]) {
                [SKANSnippet setFirstSkanCallTimestamp];
            }

            [lockObject unlock];

            if (handler) {
                handler([conversionValue intValue], error);
            }
        }] resume];
    });
}

+ (BOOL)isSkanUpdateWindowOver {
    NSInteger timeDiff = [SKANSnippet getCurrentUnixTimestamp] - [SKANSnippet getLastSkanCallTimestamp];
    return SECONDS_PER_DAY <= timeDiff;
}

@end

Métodos de utilidad

Gestión de metadatos

Los métodos de utilidad gestionan el almacenamiento de metadatos de SKAdNetwork en UserDefaults, lo cual es crítico para el cálculo del conversion value.

Implementación crítica: Estas utilidades mantienen los metadatos necesarios para un cálculo preciso del conversion value. No las modifique a menos que sea necesario.

Métodos de utilidad
+ (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];
}

+ (NSDictionary*)getTotalRevenue {
    NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
    if (![userDefaults objectForKey:TOTAL_REVENUE_BY_CURRENCY_KEY]) {
        return [NSDictionary new];
    }
    return [userDefaults objectForKey:TOTAL_REVENUE_BY_CURRENCY_KEY];
}

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

+ (void)setConversionValue:(int)value {
    if (@available(iOS 14.0, *)) {
        if (value <= [[SKANSnippet getConversionValue] intValue]) {
            return;
        }

        [SKAdNetwork updateConversionValue:value];

        NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
        [userDefaults setInteger:value forKey:CONVERSION_VALUE_KEY];
        [userDefaults synchronize];
    }
}

+ (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]; } + (void)setTotalRevenue:(NSDictionary *)values { NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults]; [userDefaults setObject:values forKey:TOTAL_REVENUE_BY_CURRENCY_KEY]; [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]; } + (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; } @end

Actualización de la integración S2S

Opcionalmente, mejore la integración server-to-server con metadatos de SKAdNetwork para la validación de la implementación y la resolución de problemas.

Actualizaciones obligatorias

Respuesta del conversion value

Una vez activado el modelo de conversión, los endpoints S2S devuelven el campo conversion_value de tipo entero que contiene el próximo valor para la actualización del lado del cliente.

Manejo de la respuesta: Analice conversion_value de las respuestas del endpoint S2S y aplíquelo al framework SKAdNetwork mediante el código del lado del cliente.


Mejoras opcionales

Parámetros de metadatos

Enriquezca las solicitudes de sesión y de evento S2S con metadatos de SKAdNetwork para validar la integración y resolver problemas de implementación.

Parámetro Descripción
skan_conversion_value Último conversion value en el momento de la solicitud
skan_first_call_timestamp Marca de tiempo Unix de la primera llamada a la API de SKAdNetwork
skan_last_call_timestamp Marca de tiempo Unix de la última llamada a la API de SKAdNetwork

Código de extracción de metadatos

Extraiga los valores de metadatos de SKAdNetwork utilizando los métodos de utilidad para la transmisión S2S.

Objective-C
NSDictionary *skanMetadata = @{
    @"skan_conversion_value":
        [[SKANSnippet getConversionValue] stringValue],
    @"skan_first_call_timestamp":
        [NSString stringWithFormat:@"%ld", [SKANSnippet getFirstSkanCallTimestamp]],
    @"skan_last_call_timestamp":
        [NSString stringWithFormat:@"%ld", [SKANSnippet getLastSkanCallTimestamp]]
};

// Forward skanMetadata to your server for S2S API enrichment

Reenvíe los parámetros al lado del servidor y añádalos a las solicitudes de la API S2S de Singular. Documentación completa de parámetros: S2S API Reference


Flujo de la integración

Flujo completo de SKAdNetwork para clientes S2S, desde la gestión de conversiones del lado del cliente hasta el procesamiento de postbacks.

Flujo de integración S2S de SKAdNetwork

Etapas del flujo

Proceso de extremo a extremo

  1. Solicitud de conversion value: El código de la app se comunica de forma síncrona con el endpoint de Singular para obtener el último conversion value según las sesiones, los eventos y los ingresos
  2. Actualización del framework: La app actualiza el framework SKAdNetwork con el conversion value recibido
  3. Enriquecimiento de metadatos: La app enriquece los eventos y las sesiones S2S con metadatos de SKAdNetwork para la validación
  4. Expiración del temporizador: Después de más de 24 horas desde la última actualización, SKAdNetwork envía el postback a la red publicitaria
  5. Reenvío del postback: La red reenvía el postback a Singular (configuración segura o regular)
  6. Procesamiento del postback: Singular procesa el postback mediante:
    • La validación de la firma criptográfica
    • La decodificación del conversion value utilizando el modelo configurado
    • El enriquecimiento con parámetros de marketing según las integraciones de partners
    • El envío de los datos decodificados a BI y a partners mediante postbacks

Separación de datos: Los datos de SKAdNetwork (instalaciones y eventos decodificados) son accesibles a través de reportes, APIs, tablas ETL y postbacks separados para evitar mezclarlos con los conjuntos de datos existentes durante las pruebas y la validación.


Implementación en el ciclo de vida de la app

Integre los métodos de SKAdNetwork en los puntos apropiados del ciclo de vida de la app para una cobertura de atribución completa.

Ejemplo de implementación

Colocación de los métodos

Objective-C
// On app launch (in applicationDidFinishLaunching)
[SKANSnippet registerAppForAdNetworkAttribution];

// After each session handled
[SKANSnippet updateConversionValueAsync:^(int value, NSError *error) {
    if (error) {
        NSLog(@"Conversion value update failed: %@", error);
    } else {
        NSLog(@"Conversion value updated to: %d", value);
    }
}];

// After handling non-revenue events
[SKANSnippet updateConversionValueAsync:@"event_name"
                   withCompletionHandler:^(int value, NSError *error) {
    if (error) {
        NSLog(@"Event conversion value update failed: %@", error);
    } else {
        NSLog(@"Event conversion value updated to: %d", value);
    }
}];

// After handling revenue events
[SKANSnippet updateRevenue:15.53 andCurrency:@"USD"];
[SKANSnippet updateConversionValueAsync:@"revenue_event_name"
                   withCompletionHandler:^(int value, NSError *error) {
    if (error) {
        NSLog(@"Revenue conversion value update failed: %@", error);
    } else {
        NSLog(@"Revenue conversion value updated to: %d", value);
    }
}];

Prueba de la implementación

Valide la implementación de SKAdNetwork utilizando el endpoint de prueba de Singular antes del despliegue en producción.

Entorno de prueba

Configuración del endpoint de prueba

Reemplace el endpoint de gestión de conversiones de producción por el endpoint de prueba para simular el flujo de la app.

URL del endpoint de prueba:

https://skadnetwork-testing.singular.net/api/v1/conversion_value
  • No se requiere API key para el endpoint de prueba
  • Devuelve los conversion values de 0 a 2 de forma secuencial
  • Devuelve una cadena vacía después de la tercera llamada

Procedimiento de prueba

Pasos de validación

  1. Actualizar el endpoint: Reemplace la constante SINGULAR_API_URL por la URL del endpoint de prueba
  2. Inicializar el conversion value: Asegúrese de que el conversion value predeterminado se inicialice en 0
  3. Generar eventos: Active 3 eventos diferentes dentro de la app
  4. Verificar las actualizaciones: Confirme que updateConversionValueAsync se llame después de cada evento
  5. Registrar los valores: Registre los conversion values devueltos para verificar la progresión correcta (0 → 1 → 2)
  6. Confirmar la finalización: Después de la tercera llamada, verifique la respuesta vacía y la retención del valor final

Comportamiento esperado:

  • Primer evento: recibe el conversion value 0
  • Segundo evento: recibe el conversion value 1
  • Tercer evento: recibe el conversion value 2
  • Cuarto evento y posteriores: respuesta vacía, el valor 2 persiste

Registro de cambios del código

Realice un seguimiento de las actualizaciones de los ejemplos de código y de las correcciones críticas aplicadas a lo largo del tiempo.

Historial de versiones

Fecha Cambios
1 de octubre de 2020
  • Corrección [CRÍTICA]: Se corrigió la inicialización del mecanismo de bloqueo en getConversionValueFromServer
  • Mejora: El motivo del fallo de la respuesta del servidor ahora se devuelve en el manejo de errores
23 de septiembre de 2020
  • Corrección [CRÍTICA]: setConversionValue ahora llama correctamente a updateConversionValue
  • Mejora: Manejo de errores mejorado al recuperar conversion values del servidor
15 de septiembre de 2020
  • Cambio [IMPORTANTE]: El valor predeterminado de getConversionValue cambió de null a 0 cuando no está establecido
  • Mejora: Mejoras en el reporte y el manejo de ingresos
  • Mejora: Implementación de la recuperación asíncrona del conversion value