Saltar al contenido principal

Integración del SDK de Unity

El SDK de Unity envuelve los SDK nativos de Joryio para iOS y Android. No añade lógica de seguimiento propia: cada llamada se reenvía al SDK nativo, así que el comportamiento, el agrupado y la gestión sin conexión son idénticos a los de una integración nativa.

Requisitos​

  • Unity 2022.3 LTS o superior. Unity 2021.3 y 2022.1 compilan Android con JDK 8 y no pueden compilar contra el SDK nativo de Android (bytecode de Java 11).
  • iOS 14.0+ / Android 6.0+ (API 23)
  • Android: Target API Level 34 o superior (Player Settings). Las librerías AndroidX del SDK nativo requieren compileSdk 34, y el build de Android de Unity falla con un valor menor.
  • Android: External Dependency Manager for Unity (EDM4U). iOS no lo usa (ver Instalación).
  • iOS: Xcode 15 o posterior, con acceso de red a github.com la primera vez que se compila el proyecto exportado - Xcode descarga el SDK nativo como paquete Swift.
  • Para push en Android: Firebase Cloud Messaging en el juego - el paquete Messaging del Firebase Unity SDK y tu google-services.json. El SDK nativo de Android se compila contra firebase-messaging pero no lo incluye; sin él, la analítica y los mensajes in-app funcionan y el push queda desactivado.
Plataformas admitidas

Solo iOS y Android. En las demás plataformas - el Editor, escritorio, consolas - todos los métodos existen y no hacen nada, y el SDK registra un aviso por sesión. El código de tu juego nunca necesita #if UNITY_IOS || UNITY_ANDROID alrededor de una llamada a Joryio.

Instalación​

Añade el paquete (com.joryio.sdk) desde Git: en el Package Manager elige + → Add package from git URL e introduce la URL con la etiqueta de la versión:

https://github.com/joryio/joryio-unity-sdk.git#1.3.3

Para actualizar, cambia la etiqueta a la nueva versión. Fija siempre una: sin ella el Package Manager sigue el commit más reciente.

O descarga joryio-unity-<version>.unitypackage desde la página de releases en GitHub e impórtalo con Assets → Import Package → Custom Package. El SDK queda en Assets/JoryioSDK; para actualizar, borra esa carpeta e importa la nueva versión.

Usa un solo método de instalación, no los dos. El paquete por Git URL y Assets/JoryioSDK llevan los mismos assemblies (Joryio.Runtime, Joryio.Editor) y los mismos GUID de assets, así que un proyecto con ambos no compila. Para cambiar de método, quita primero el que tienes: borra Assets/JoryioSDK o elimina el paquete Joryio en el Package Manager. El .unitypackage también trae a Assets/JoryioSDK los términos de licencia (LICENSE), README.md y CHANGELOG.md.

El paquete no está en OpenUPM, que solo publica paquetes de código abierto; los SDK de Joryio tienen licencia comercial.

El paquete no incluye binarios nativos precompilados, de modo que las librerías compartidas se reconcilian con lo que tu proyecto ya use. Trae los SDK nativos sin UI - tracking, identidad y push, pero no las vistas nativas de mensajes in-app (ver Mensajes in-app):

  • Android: deja que EDM4U resuelva io.joryio:joryio-android con Gradle, desde el manifiesto de dependencias del paquete (Editor/JoryioDependencies.xml).
  • iOS: nada que resolver en Unity. Al compilar para iOS, el paquete añade el SDK nativo al proyecto de Xcode exportado como paquete Swift (https://github.com/joryio/joryio-ios-sdk, exactamente en la versión del paquete) y enlaza su product Joryio en UnityFramework. Xcode lo descarga y lo resuelve la primera vez que abre o compila el proyecto. Ni CocoaPods ni EDM4U intervienen en iOS.
Actualizar desde una versión que usaba CocoaPods

Las versiones anteriores tomaban el SDK de iOS de CocoaPods (el pod Joryio/Core), y CocoaPods trunk deja de aceptar versiones nuevas el 2 de diciembre de 2026. Tras actualizar, compila el player de iOS una vez con Replace en lugar de Append, para que el proyecto exportado deje de llevar el pod antiguo, y borra cualquier línea <iosPod name="Joryio/..."> de tus propios archivos de dependencias de EDM4U.

Inicio rápido​

Cada plataforma tiene su propia SDK key (jry_sdk_ios_..., jry_sdk_android_...), en Settings → Apps del dashboard. Usa el host de API que se muestra para tu workspace.

using Joryio.Runtime;

void Awake()
{
#if UNITY_IOS
const string sdkKey = "jry_sdk_ios_YOUR_IOS_KEY";
#else
const string sdkKey = "jry_sdk_android_YOUR_ANDROID_KEY";
#endif
JoryioSDK.Initialize(sdkKey, "api-eu1.joryio.com");
}

Después registra lo que hacen los jugadores:

JoryioSDK.Track("level_complete", new Dictionary<string, object> {
{ "level", 7 },
{ "duration_seconds", 143.5 },
});

JoryioSDK.Identify("player-1234");
JoryioSDK.SetAttribute("favourite_class", "mage");
JoryioSDK.IncrementAttribute("games_played");

// Another identifier for the same player - works before Identify too
JoryioSDK.AddAlias("steam_id", "76561198000000000");

Los valores conservan su tipo al cruzar la frontera: { "level", 7 } se guarda como el número 7, no como "7". Los segmentos comparan numéricamente, así que un número convertido en texto no coincidiría con nadie. Se conserva cualquier tipo numérico, los diccionarios y listas anidados siguen siendo objetos y arrays, DateTime se envía como ISO-8601 en UTC, y NaN o infinito pasa a ser null.

Como valor de atributo (SetAttribute, SetAttributes, IncrementAttribute), NaN o infinito se ignora con un aviso y el atributo queda sin cambios - no se borra. La conversión a null solo se aplica dentro de las propiedades de un evento.

Ninguna llamada lanza una excepción dentro de tu juego: una llamada antes de Initialize, un argumento obligatorio null o un fallo nativo se registra y se ignora.

La excepción es el consentimiento: OptOut(), OptIn() y UpdateConsent() funcionan antes de Initialize. La decisión se aplica cuando arranca el SDK, antes de enviar nada. IsUserOptedOut() también responde antes de Initialize - consulta Consentimiento. WipeData() y GetTrackingConsent() también funcionan antes de Initialize, igual que SetSdkAuthenticationToken y SetSdkAuthenticationErrorHandler - consulta Autenticación del SDK. También los cambios de suscripción y UnregisterPush(): SetSubscription, AddToSubscriptionGroup, RemoveFromSubscriptionGroup y UnregisterPush() llamados antes de Initialize se guardan en el dispositivo, incluso tras un reinicio, y se envían cuando el SDK arranca. Un cambio más reciente del mismo canal o grupo sustituye al anterior, así que se envía la última elección del jugador. Una retirada (una baja, salir de un grupo, UnregisterPush()) se envía siempre: un OptOut(), un consentimiento Denied o WipeData() antes de Initialize descartan las altas guardadas, nunca las retiradas, y una retirada hecha antes de un WipeData() se envía para el jugador que la hizo, no para la identidad nueva que crea el borrado. Un RegisterPushToken antes de Initialize no se envía y no cancela un UnregisterPush() guardado. Una suscripción, un opt-in o la entrada en un grupo hechos antes de Initialize mientras el jugador está dado de baja del seguimiento (opt-out) o el consentimiento es Denied (guardado de un arranque anterior o fijado en este) no se guardan: se descartan con un aviso, y un OptIn() o Granted posterior no los envía.

Un valor null en un atributo no es un error: SetAttribute("clan", null) borra el atributo, igual que UnsetAttribute("clan"), en iOS y en Android por igual.

Configuración​

Cada ajuste es opcional y refleja la opción del mismo nombre de los SDK nativos. Las duraciones son segundos y lo dicen en su nombre, porque los nativos no coinciden (iOS toma segundos, Android milisegundos) - los puentes convierten.

JoryioSDK.Initialize(sdkKey, "api-eu1.joryio.com", new JoryioConfig {
Debug = UnityEngine.Debug.isDebugBuild,
FlushIntervalSeconds = 10,
AnonymousId = existingDeviceId, // only if you already have one
});
PropiedadTipoPredeterminadoDescripción
DebugboolfalseRegistro nativo detallado. El nivel de registro nativo lo sigue: debug cuando es true, solo errores en caso contrario.
BatchSizeint50Eventos por lote antes de un vaciado automático.
FlushIntervalSecondsdouble5Segundos entre vaciados automáticos.
SendImmediatelyboolfalseEnvía cada evento en cuanto se registra, sin lotes (no recomendado en producción).
MaxQueueSizeint1000Máximo de eventos que retiene la cola local mientras fallan los envíos; por encima se descartan los más antiguos.
RequestTimeoutSecondsdouble10Tiempo de espera HTTP por petición.
SessionTimeoutSecondsdouble1800Segundos de inactividad antes de que empiece una sesión nueva.
TrackSessionStartbooltrueEmite automáticamente un evento de inicio de sesión.
AnonymousIdstringgeneradoTu propio id anónimo para este dispositivo. Se usa en esta ejecución y se guarda solo cuando aún no hay nada guardado, así un id definido una vez sobrevive aunque el juego deje de pasar la opción.
RequestPushPermissionAtLaunchboolfalsePide el permiso de push durante Initialize. Más abajo se explica por qué está desactivado por defecto.
MaxRetriesint3Reintentos por petición antes de esperar al siguiente vaciado.
RetryBackoffMsint1000Retardo base entre reintentos, en milisegundos (el nombre de los SDK nativos).
TrackingConsentTrackingConsent?null (sin definir)La respuesta de tu pantalla de consentimiento al arrancar. Sin definir conserva la respuesta que recuerda el SDK (un Denied guardado sigue denegado) y, si no hay ninguna, el seguimiento es normal. Granted hace seguimiento y sustituye una denegación guardada; Pending no envía nada hasta UpdateConsent(TrackingConsent.Granted), solo durante este arranque; Denied no recopila nada y se recuerda. Consulta Consentimiento.
OptOutboolfalseEmpieza con el opt-out activado. El opt-out se guarda, así que false no deshace un OptOut() anterior.
NotificationChannelNamestringnull ("Notifications")Solo Android. El nombre del canal de notificaciones en los ajustes de notificaciones del sistema (Android 8+); pásalo traducido. iOS no tiene canales y lo ignora.
EnableSdkAuthenticationboolfalseEnvía un token firmado con cada petición. Consulta Autenticación del SDK.
SdkAuthenticationTokenstringningunoEl token inicial, si ya tienes uno.
AllowHtmlJsInAppMessagesboolfalsePermite que la UI nativa dibuje mensajes HTML. Sus scripts no se ejecutan; el puente del SDK nativo gestiona botones y formularios. No afecta a los mensajes que dibuja tu propio handler.
HoldInAppMessagesForHandlerboolfalseDesde la versión 1.3.2. Tu juego dibuja los mensajes in-app con su propio handler: retiene los mensajes que estén listos antes de llamar a SetInAppMessageHandler. Ver Antes de configurar tu handler.

Debug ajusta tanto el nivel de registro nativo como el modo debug nativo en iOS y Android, lo que además habilita las llamadas de prueba ResetDisplayedCampaigns y EvaluateInAppCampaigns.

Notificaciones push​

Si tu juego aún no tiene una capa de push, el SDK registra el dispositivo por su cuenta - en iOS siempre, y en Android cuando Firebase Cloud Messaging está en el juego (consulta Requisitos). Sin Firebase en Android no hay token de FCM, así que no se registra nada y el push queda desactivado; la analítica y los mensajes in-app no se ven afectados. Pide el permiso cuando el jugador tenga un motivo para aceptar: un aviso en los primeros segundos de partida es el que más se rechaza, y una negativa es mucho más difícil de revertir que un retraso.

JoryioSDK.RequestPushPermission(granted => {
Debug.Log(granted ? "Push enabled" : "Player declined");
});

El callback se ejecuta en el hilo principal, exactamente una vez. En iOS el SDK recibe después el token de APNs por sí mismo: no hay app delegate que editar.

En Android el aviso se muestra desde la activity del juego: GameActivity (la predeterminada en Unity 6) o la clásica UnityPlayerActivity (versiones anteriores, o un proyecto de Unity 6 que la elija). En ambas, la respuesta del jugador se informa cuando se cierra el diálogo del sistema. Por debajo de Android 13 no hay permiso en tiempo de ejecución y el callback responde al momento si las notificaciones están activadas.

Si prefieres que el SDK lo pida durante Initialize - una build de soft launch, un título en el que las notificaciones son el objetivo, o simplemente para ver el flujo - actívalo en la configuración:

JoryioSDK.Initialize(sdkKey, "api-eu1.joryio.com", new JoryioConfig {
RequestPushPermissionAtLaunch = true,
});

Está desactivado por defecto, no ausente. El aviso es de una sola oportunidad en ambas plataformas, así que el valor por defecto protege el caso habitual; esto está aquí para cuando hayas decidido otra cosa.

Si ya usas Firebase o Unity Mobile Notifications​

La mayoría de los juegos lo hacen, y dos capas de push compitiendo por el mismo token es la forma habitual de romper una integración. Conserva la tuya y pásanos el token - el token de dispositivo de APNs en iOS, el token de registro de FCM en Android. En Android ese token viene de Firebase Messaging (o de tu propio wrapper de FCM): Unity Mobile Notifications no genera un token de FCM en Android - allí programa notificaciones locales - así que solo puede aportar el token en iOS:

JoryioSDK.RegisterPushToken(tokenFromYourExistingStack);

Añade un callback para saber cómo fue - isRetryable indica un fallo transitorio (sin conexión, DNS, 5xx) que merece un nuevo registro, isUnauthorized indica una clave del SDK incorrecta:

JoryioSDK.RegisterPushToken(tokenFromYourExistingStack, result =>
{
if (!result.success && result.isRetryable)
{
// transitorio (sin conexión, DNS, 5xx): vuelve a registrar cuando vuelva la red
}
});

En Android el SDK ya no toca el servicio FCM. En iOS todavía ocurren dos cosas, para que nada te sorprenda:

  • Initialize convierte al SDK en el delegate de UNUserNotificationCenter y reenvía cada callback al delegate que estaba instalado antes. Inicializa Joryio después de que tu otro plugin de push fije su delegate (Unity Mobile Notifications lo hace al arrancar, así que Awake sirve). Un plugin que fije el delegate más tarde sustituye al de Joryio, y entonces el registro de aperturas depende de tu llamada a TrackPushClick.
  • El SDK recibe el token de APNs del app controller de Unity por sí mismo. Pasar el mismo token también a RegisterPushToken no hace daño - salvo que el jugador haya desactivado el push, porque RegisterPushToken anula un UnregisterPush() (ver más abajo).

RegisterPushToken vuelve a activar el push en este dispositivo: anula un UnregisterPush() anterior. El OnTokenReceived de Firebase se dispara solo en cada arranque, así que si lo reenvías, omítelo mientras el jugador tenga el push desactivado en tus ajustes, y llama a RegisterPushToken cuando lo vuelva a activar. Si no, es una renovación del token, y no el jugador, lo que deshace su elección.

UpdateBadgeCount(count) y ClearBadge() ajustan el globo del icono de la app en iOS. En Android no hacen nada, porque no existe una API de globos propia.

Mensajes in-app​

El manifiesto de dependencias trae los SDK nativos sin UI, así que por defecto nada dibuja los mensajes in-app: hasta que instales un handler o añadas la UI nativa, las campañas llegan al dispositivo y no se muestran. Elige una de las dos opciones.

Renderízalos dentro del motor con tu propio arte (lo recomendado para juegos) - instala un handler:

JoryioSDK.SetInAppMessageHandler(message => {
// Title / Body / ImageUrl / Buttons are the authored content.
// Name is the campaign's dashboard label - never copy to show a player.
myCanvas.Show(message.Title, message.Body, message.ImageUrl);
foreach (var button in message.Buttons)
myCanvas.AddButton(button.Text, () => {
message.MarkClicked(button);
if (button.Action == "url" || button.Action == "deep_link")
Application.OpenURL(button.Url);
else if (button.Action == "request_push_permission")
JoryioSDK.RequestPushPermission(granted => { });
myCanvas.Hide();
message.MarkDismissed(); // the tap closed the message
});
message.MarkDisplayed();
}, canRenderHtml: false);
Ahora el reporte es responsabilidad tuya

Instalar un handler hace que el SDK deje de dibujar, y desde ese momento no puede ver qué pasó. Llama a MarkDisplayed() cuando aparezca el mensaje, y a MarkClicked(button) / MarkDismissed() según actúe el jugador. MarkDisplayed() informa una vez por mensaje por muchas veces que se llame, y MarkClicked(button) envía el Id del botón como elementId del clic para que el mapa de clics distinga los botones. Un botón sin id no envía elementId - nunca su etiqueta, que es texto de pantalla.

Un toque que cierra el mensaje se informa como ambas cosas: MarkClicked(button) y después MarkDismissed(), como envían los SDK nativos cuando dibujan el mensaje ellos mismos.

Un mensaje que nunca se informa cuenta como nunca mostrado: ninguna visualización en los informes, nada con lo que la atribución de ingresos pueda cruzarlo, y los límites de frecuencia no lo cuentan. La campaña parece entonces no haber llegado a nadie, lo que parece un problema de segmentación y no de integración. El SDK registra un aviso si un mensaje se descarta sin informar.

Para devolver el dibujo al SDK nativo - cuando tu renderer se destruye, o en una escena sin UI donde dibujar - quita el handler:

JoryioSDK.ClearInAppMessageHandler(); // igual que SetInAppMessageHandler(null)

El SDK nativo vuelve entonces a dibujar los mensajes con sus propias vistas (si sus artefactos de UI están en el build - ver más abajo) y comunica al servidor sus propias capacidades. Es el clearInAppMessageCallback de los SDK nativos.

Desde un componente que quizá no sea el único renderer - uno por escena, por ejemplo - pasa el handler que instalaste. Solo se quita si sigue siendo el instalado, nunca un handler que otro script instaló después:

void OnDestroy() => JoryioSDK.ClearInAppMessageHandler(myHandler);

canRenderHtml describe tu renderizador. Un canvas normal de Unity no puede mostrar contenido HTML, que necesita una vista web. Esto no es cosmético: las campañas se segmentan según lo que informa el dispositivo, y una capacidad no declarada se interpreta como "no puede", no como "se desconoce". Declarar HTML que no puedes dibujar envía campañas a jugadores que nunca las verán; omitir HTML que sí puedes dibujar te priva de campañas que podrías haber mostrado.

Si tu vista web muestra un mensaje HTML con un <form data-jry-form>, envíalo a través del SDK para que los valores se escriban en el perfil del jugador. Los valores son cadenas, o una lista de cadenas para un grupo de casillas:

JoryioSDK.SubmitInAppForm(message.Id, new Dictionary<string, object> {
{ "email", "[email protected]" },
{ "topics", new List<string> { "events", "offers" } },
}, result => {
if (!result.ok) Debug.Log(result.message);
});

El callback siempre se ejecuta una vez; cualquier fallo, incluida la falta de red, es ok = false.

Antes de configurar tu handler​

Desde la versión 1.3.2. SetInAppMessageHandler solo se puede llamar después de Initialize, y la primera sincronización puede terminar antes - entonces el SDK nativo, si su UI está en el build, dibuja ese mensaje por su cuenta. Configura HoldInAppMessagesForHandler = true en JoryioConfig para evitarlo: un mensaje que esté listo antes de configurar tu handler se retiene - no se dibuja, no se informa y no cuenta para ningún límite de frecuencia - y se entrega a tu handler en el momento en que lo configuras. ClearInAppMessageHandler(), en su lugar, termina la retención y el SDK nativo dibuja el mensaje retenido.

O deja que el SDK nativo los dibuje​

Para que el SDK nativo dibuje los mensajes él mismo - una capa con el estilo del sistema operativo sobre tu juego, con reporte automático y sin código - añade sus artefactos de UI.

Android: añade el artefacto de UI en un archivo de dependencias de EDM4U propio, por ejemplo Assets/Editor/JoryioUIDependencies.xml, con la misma versión que la del paquete:

<dependencies>
<androidPackages>
<androidPackage spec="io.joryio:joryio-android-ui:1.3.3" />
</androidPackages>
</dependencies>

iOS: añade el scripting define symbol JORYIO_IOS_UI en Player Settings → iOS → Other Settings → Scripting Define Symbols. La compilación de iOS enlaza entonces también el product JoryioUI del paquete Swift, en la versión del paquete.

Un handler instalado con SetInAppMessageHandler sigue teniendo prioridad sobre las vistas nativas.

Qué contiene un mensaje​

El handler recibe todo lo que envió el servidor, con los mismos nombres de campo que usa el SDK de React Native (antes recibía solo Id y Name, con lo que el juego no tenía nada que dibujar salvo la etiqueta interna de la campaña):

CampoPresente cuandoSignificado
Id, Name, TypesiempreId de la campaña, nombre en el panel (no es texto para mostrar) y modal / banner / slideup / fullscreen / custom.
Kindsiemprenative o html - cuál de los grupos siguientes está relleno. IsNative / IsHtml son atajos.
Title, Body, ImageUrlnativeEl texto y la imagen redactados. Title e ImageUrl pueden ser null; Body siempre está.
ButtonsnativeNunca null. Cada botón tiene Id, Text, Action (dismiss, url, deep_link, request_push_permission) y Url para las dos acciones de enlace. El SDK no puede actuar sobre un botón que dibujas tú: abre la URL por tu cuenta, llama a RequestPushPermission para la acción de primer e informa del toque con MarkClicked(button).
CloseButton, BackdropDismissiblenativeSi el autor pidió un control de cierre y si tocar fuera del mensaje lo cierra.
Html, CsshtmlEl documento. Un canvas uGUI no puede renderizarlo - declara canRenderHtml: true solo si tu juego aloja un web view.

Lo que Unity no puede renderizar, dicho claramente. El contenido HTML necesita un web view que el motor no ofrece, y los estilos del autor (colores, radio de esquinas, fuente) todavía no se reenvían a Unity - se aplica el estilo de tu propio renderer. Las imágenes son URL: descárgalas tú (UnityWebRequestTexture).

Empieza por el sample​

El paquete incluye el sample In-app renderer: una escena, un prefab que puedes rediseñar y el script que los conecta. Impórtalo desde el Package Manager (selecciona el paquete Joryio, abre Samples y pulsa Import), pon tus SDK keys en el objeto JoryioInApp de JoryioInAppSample.unity y compila para un dispositivo iOS o Android. Los mensajes solo llegan en un build de dispositivo: en el Editor el SDK no hace nada, así que darle a Play no muestra nada.

¿Instalaste desde el .unitypackage? No hay una entrada del Package Manager desde la que importar: descarga joryio-unity-<version>-sample.unitypackage desde la misma página de releases e impórtalo igual. Queda en Assets/Samples/Joryio/<version>/In-app renderer, donde el Package Manager pone el sample, y también necesita com.unity.ugui.

El prefab es la parte que haces tuya - cambia los colores, la fuente, añade tu propio arte. El renderer dibuja lo que haya en su campo Panel Prefab (JoryioInAppPanel.prefab en la escena del sample) y rellena cuatro hijos que busca por nombre: Title y Body (Text), PrimaryButton (copiado una vez por cada botón de la campaña) y SecondaryButton (el botón de cerrar). Los ajustes de estilo del autor se aplican encima donde la campaña los definió. Con el campo vacío, construye un panel sencillo por código.

Los botones de uGUI necesitan un EventSystem, así que el renderer crea uno cuando la escena no tiene ninguno - con el módulo de UI del Input System cuando ese es tu manejo de entrada activo, y si no con el antiguo. El script muestra lo único que hay que conservar: informar del ciclo de vida de cada mensaje. Requiere com.unity.ugui, que el SDK en sí no necesita.

Cuándo un mensaje cuenta como mostrado​

Un mensaje cuenta como mostrado - se envía in_app.displayed y se consume el límite de frecuencia de la campaña - solo cuando la persona pudo verlo de verdad. El tiempo que debe estar en pantalla es IN_APP_DISPLAY_DWELL_MS (1000 ms), la misma constante en todos los SDK de Joryio.

  • Los mensajes que dibuja el SDK nativo siguen la regla de iOS y Android: se muestran solo en una pantalla estable en primer plano y cuentan después de 1 segundo en pantalla con la app en primer plano. Un toque o un cierre antes cuenta como visto; un mensaje que sale de la pantalla antes no cuenta y sigue siendo elegible.
  • Si el juego dibuja los mensajes él mismo, llama a MarkDisplayed() con la misma regla - JoryioSDK.IN_APP_DISPLAY_DWELL_MS: cuando el mensaje lleve visible ese tiempo, o justo antes de MarkClicked / MarkDismissed cuando el jugador actúe antes. No lo llames para un mensaje que salió antes de la pantalla (un cambio de escena, la app en pausa).

Dos interruptores independientes, iguales en todos los SDK de Joryio:

  • Consentimiento de seguimiento - la respuesta de tu pantalla de consentimiento: Granted, Denied o Pending.
  • Opt-out - la elección del propio jugador: OptOut() / OptIn().

Solo se recopilan datos cuando el consentimiento está concedido y el jugador no ha hecho opt-out; cualquier "no" prevalece. Ningún interruptor cambia el otro: UpdateConsent(TrackingConsent.Granted) no levanta un opt-out (solo OptIn() lo hace), y OptOut() no cambia el consentimiento.

Si el jugador aún no ha respondido a tu pantalla de consentimiento, empieza con el consentimiento Pending: no se envía nada - ni inicio de sesión, ni sincronización in-app, ni registro de push - y los eventos esperan en el dispositivo (hasta MaxQueueSize) hasta que llegue la respuesta.

JoryioSDK.Initialize(sdkKey, apiHost, new JoryioConfig {
TrackingConsent = TrackingConsent.Pending,
});
// cuando el jugador responda
JoryioSDK.UpdateConsent(TrackingConsent.Granted); // envía lo que esperaba
// o
JoryioSDK.UpdateConsent(TrackingConsent.Denied); // lo descarta y no recopila nada
  • Denied se recuerda entre reinicios. Solo lo levanta UpdateConsent(TrackingConsent.Granted) o un TrackingConsent = TrackingConsent.Granted explícito en la configuración.
  • Dejar JoryioConfig.TrackingConsent sin definir (el valor por defecto) conserva una denegación guardada; sin nada guardado el SDK hace seguimiento normal, así que un juego sin pantalla de consentimiento no necesita ningún ajuste.
  • Pending se aplica solo al arranque actual y nunca prevalece sobre un Denied guardado.
  • Un valor fuera del enum (un cast erróneo) se ignora con un aviso; en la configuración se trata como sin definir.
JoryioSDK.OptOut(); // dejar de recopilar; los eventos en cola y las escrituras de atributos pendientes se descartan
JoryioSDK.OptIn(); // reanudar
JoryioSDK.WipeData(); // borrar todo lo almacenado en el dispositivo (el opt-out se conserva)

El opt-out se guarda y sobrevive a los reinicios. Para empezar una sesión ya con el opt-out activado, usa JoryioConfig.OptOut = true.

IsUserOptedOut() refleja solo el interruptor de opt-out, no el consentimiento. GetTrackingConsent() devuelve el interruptor de consentimiento: Granted, Denied o Pending. Los dos responden también antes de Initialize, con lo que hay guardado en el dispositivo: un opt-out o un Denied recordado de un arranque anterior se informa como tal, no como un valor por defecto, y un OptOut() / OptIn() hecho antes en el mismo arranque prevalece sobre el opt-out guardado. GetTrackingConsent() devuelve Granted solo cuando no se ha decidido nada, que es también como se comporta el SDK en ese caso.

Cuando cambia el opt-out, el SDK lo registra en el perfil del jugador como el atributo booleano tracking_opted_out, para que los segmentos puedan excluir a quienes hicieron opt-out. GetTrackingConsent() informa del consentimiento que guarda el SDK nativo, incluido un Denied recordado de un inicio anterior.

Autenticación del SDK​

El token demuestra quién es un usuario identificado: las solicitudes que indican un ID de usuario deben llevar uno válido. Los visitantes anónimos no tienen token que mostrar, así que sus solicitudes se aceptan sin él, salvo que el workspace active también la identidad estricta (strict identity).

En los workspaces con la autenticación del SDK activada, cada petición lleva un token firmado por tu servidor:

JoryioSDK.Initialize(sdkKey, apiHost, new JoryioConfig { EnableSdkAuthentication = true });
JoryioSDK.SetSdkAuthenticationToken(tokenFromYourServer); // tras el inicio de sesión

JoryioSDK.SetSdkAuthenticationErrorHandler(error => {
// error.reason: missing, invalid, expired, sub_mismatch o unknown
// error.refreshExhausted: el SDK dejó de reintentar esta petición
FetchFreshToken(token => JoryioSDK.SetSdkAuthenticationToken(token));
});

SetSdkAuthenticationToken y SetSdkAuthenticationErrorHandler también funcionan antes de Initialize. El SDK nativo arranca con ese token, antes de su primera petición, y lo conserva tras un WipeData() hecho antes de Initialize, así que una baja retenida para el jugador que se borra sigue saliendo con su token. También puedes pasar el primer token en JoryioConfig.SdkAuthenticationToken.

El handler se ejecuta en el hilo principal. Se llama ante un 401 de autenticación en las peticiones del jugador actual, también antes de que se haya definido ningún token (reason missing) - nunca por los restos de un jugador que ha cerrado sesión, así que nunca te lleva a cerrar la sesión del jugador que juega ahora. Los eventos y escrituras rechazados del jugador actual se conservan y se reintentan cuando defines un token nuevo. Los eventos salen en lotes de un solo jugador cada vez, y los eventos y escrituras de un jugador que ha cerrado sesión se descartan ante cualquier 401 de autenticación (missing, invalid, expired o sub_mismatch), para que nunca retengan los del siguiente jugador. Reset() saca el token de la sesión; se queda con los pendientes del jugador saliente, y las peticiones del siguiente jugador nunca lo llevan - fija su token con SetSdkAuthenticationToken cuando inicie sesión. Los eventos y escrituras que el jugador saliente ya había puesto en cola siguen enviándose con su propio token, así que un cierre de sesión normal no pierde nada.

E-commerce​

Para el comercio usa el tracker tipado en lugar de Track. Aplica los nombres canónicos de eventos que leen los informes de ingresos y la atribución; un nombre escrito a mano como "Added to Cart" se guarda como otro evento y no coincide con ningún informe.

var item = new EcommerceCartItem { ProductId = "gems_500", Name = "500 gems", Price = 4.99, Quantity = 1 };
JoryioSDK.Ecommerce.AddToCart(item, cartValue: 4.99);
JoryioSDK.Ecommerce.Purchase(new EcommerceOrder {
OrderId = "order-1042",
Items = new List<EcommerceCartItem> { item },
Value = 4.99,
Currency = "USD",
});

El tracker tiene los mismos veinte eventos que el SDK de React Native, de ViewProduct a SubmitReview.

Una llamada a la que le falta un argumento obligatorio registra un aviso y no envía nada - nunca un evento vacío o a cero: un producto sin ProductId, un pedido sin OrderId o Items, un ID de pedido o de cupón vacío, una llamada de carrito sin su lista de artículos. Un importe NaN o infinito (Value, Price, cartValue, Shipping, Tax, Discount, refundAmount, discountAmount) se rechaza igual, en lugar de registrarse como 0.

Identidad y sesión​

Envía los números de teléfono con + y el código de país

Envía siempre los números de teléfono en formato internacional completo (E.164): +, el código de país y luego el número - por ejemplo +972501234567. Un número sin + se lee con el país predeterminado para números de teléfono del espacio de trabajo; si no hay ninguno, o los dígitos se pueden leer de dos formas, se guarda sin verificar y no se usa para mensajes. Cómo se tratan los números de teléfono

var (userId, anonymousId) = JoryioSDK.GetIdentity(); // userId es null si el jugador es anónimo
var (sessionId, sessionStart, duration) = JoryioSDK.GetSessionInfo(); // duration en segundos

Reset() cierra la sesión del jugador: un id anónimo nuevo, sin id de usuario.

Referencia de la API​

Todos los miembros públicos de JoryioSDK, con sus nombres de parámetro. Todos son estáticos y seguros antes de Initialize (registran un aviso y no hacen nada, y un callback sigue respondiendo una vez).

Excepción: las llamadas de consentimiento, WipeData() y las de autenticación del SDK sí surten efecto antes de Initialize, y las llamadas de suscripción y UnregisterPush() se guardan hasta entonces.

MétodoQué hace
Initialize(sdkKey, apiHost, config = null)Inicia el SDK. Una segunda llamada se ignora. Una clave de otra plataforma (las claves de iOS empiezan por jry_sdk_ios_, las de Android por jry_sdk_android_) se rechaza con un error y el SDK queda apagado. Ver Configuración.
IsInitializedSi Initialize tuvo éxito. True también en un arranque de iOS antes del primer desbloqueo del dispositivo: el SDK nativo termina de arrancar al desbloquearse, y las llamadas hechas mientras tanto lo esperan. Si en el dispositivo quedan llamadas guardadas por un arranque anterior, iOS las mantiene ilegibles también hasta ese desbloqueo: el SDK no recoge nada y cada llamada espera hasta poder leerlas, y entonces se envían primero, y después las llamadas hechas desde entonces, en orden. Mientras tanto GetTrackingConsent() y IsUserOptedOut() informan de las decisiones de este arranque (llamadas y JoryioConfig). Sin ninguna, GetTrackingConsent() informa Pending, porque el consentimiento guardado todavía no se puede leer, e IsUserOptedOut() responde desde el SDK nativo, que puede no ver hasta el desbloqueo un opt-out guardado por un arranque anterior. Cuando se envían las llamadas, se aplica el consentimiento que este arranque haya fijado explícitamente (UpdateConsent antes de Initialize, si no JoryioConfig.TrackingConsent); sin él, se aplica la decisión guardada en el dispositivo, leída después del desbloqueo, igual que en los SDK nativos: un Denied guardado sigue en Denied y, si no hay ninguna guardada, el SDK queda en Granted, como en cualquier otro arranque. Otros getters, como GetUserId(), GetAnonymousId() y GetSessionId(), siguen respondiendo desde el SDK nativo y todavía no reflejan las llamadas que esperan.
SdkVersionLa versión del SDK, "1.3.3".
Track(eventName, properties = null)Registra un evento.
TrackScreen(screenName, properties = null)Registra una vista de pantalla.
Flush()Envía ahora los eventos en cola.
Identify(userId)Identifica al jugador. Mientras el jugador se ha dado de baja del seguimiento o el consentimiento es Denied, el id solo se recuerda en el dispositivo (no se envía nada), para que una baja hecha mientras tanto llegue al jugador correcto.
Reset()Cierra sesión: un id anónimo nuevo, sin id de usuario.
AddAlias(label, name)Asocia otro identificador, como ("steam_id", "7656..."). Funciona también para un jugador anónimo.
GetUserId() / GetAnonymousId()Los ids actuales ("" si no hay).
GetIdentity()(userId, anonymousId); userId es null para un jugador anónimo.
SetAttributes(attributes)Define varios atributos. Un valor null borra ese atributo.
SetAttribute(key, value)Define un atributo conservando su tipo. Un valor null lo borra; NaN o infinito se ignora.
IncrementAttribute(key, by = 1)Suma a un atributo numérico. NaN o infinito se ignora.
UnsetAttribute(key)Borra un atributo.
UpdateConsent(consent)TrackingConsent.Granted, Denied o Pending. Nunca cambia el opt-out. Funciona también antes de Initialize. Ver Consentimiento.
OptOut() / OptIn()Detiene o reanuda la recogida. Se guarda entre reinicios; nunca cambia el consentimiento. Funcionan también antes de Initialize.
IsUserOptedOut()Si el jugador ha hecho opt-out (solo el interruptor de opt-out). Responde también antes de Initialize, con la decisión guardada.
GetTrackingConsent()El consentimiento actual: TrackingConsent.Granted, Denied o Pending (solo el interruptor de consentimiento). Responde también antes de Initialize, con la decisión guardada.
WipeData()Borra todo lo guardado en el dispositivo. El opt-out se conserva.
SetSubscription(channel, status)Suscripción de marketing para un canal. Ver Suscripción de marketing.
AddToSubscriptionGroup(groupId, channel) / RemoveFromSubscriptionGroup(groupId, channel)Entra en un grupo de suscripción o sale de él.
RequestPushPermission(onResult)Pide el permiso de notificaciones; onResult(bool granted).
GetPushPermissionStatus(onResult)"granted", "denied", "notDetermined" o "unavailable".
IsPushEnabled(onResult)Si las notificaciones están activadas; onResult(bool).
RegisterPushToken(token, onResult = null)Entrega un token de tu propio stack de push; onResult(PushRegistrationResult).
ReportPushDelivered(trackingId)Informa de que llegó un push (tu propio stack de push).
TrackPushClick(trackingId)Informa de que se abrió una notificación.
UnregisterPush()Detiene el push a este dispositivo. Sigue apagado tras un reinicio hasta RegisterPushToken (después de Initialize) o una solicitud de permiso. Se guarda si se llama antes de Initialize.
GetDeviceToken()El token de push que guarda el SDK ("" si no hay).
UpdateBadgeCount(count) / ClearBadge()Badge del icono de la app en iOS; no hacen nada en Android.
GetSessionId()El id de la sesión actual.
GetSessionInfo()(sessionId, sessionStart, duration); duration en segundos.
SetSdkAuthenticationToken(token)Proporciona un token firmado. Ver Autenticación del SDK.
SetSdkAuthenticationErrorHandler(handler)Avisa cuando se rechaza una petición con autenticación del SDK; null deja de escuchar.
EcommerceEl tracker tipado de e-commerce. Ver E-commerce.
SetInAppMessageHandler(handler, canRenderHtml = false)Dibujas tú los mensajes in-app. null devuelve el dibujo al SDK nativo.
ClearInAppMessageHandler()Devuelve el dibujo de los mensajes in-app al SDK nativo.
ClearInAppMessageHandler(handler)Lo mismo, solo si handler sigue siendo el instalado. Un handler null quita el que esté instalado.
TrackInAppImpression(campaignId, action, elementId = null)Informa displayed, clicked o dismissed; elementId es el id del botón pulsado. Mejor usa los métodos Mark... del mensaje.
SubmitInAppForm(campaignId, values, onResult)Envía un formulario de un mensaje HTML que dibujas tú; onResult(InAppFormSubmitResponse).
SyncInAppCampaigns()Descarga ahora las campañas más recientes.
EvaluateInAppCampaigns() / ResetDisplayedCampaigns()Ayudas para pruebas; se ignoran salvo que Debug esté activado.

En el mensaje que recibe tu handler: MarkDisplayed(), MarkClicked(button = null) y MarkDismissed().

Compilación​

El SDK configura automáticamente los proyectos exportados. No hay nada que hacer a mano:

  • Xcode: el SDK nativo como paquete Swift (joryio-ios-sdk, enlazado en UnityFramework), versión de Swift, incrustación del runtime de Swift, la capacidad de push, el modo en segundo plano de notificaciones remotas y UNITY_USES_REMOTE_NOTIFICATIONS en Classes/Preprocessor.h - sin él, Unity elimina el callback que entrega el token de APNs. La capacidad de push se añade al archivo de entitlements existente del target cuando lo tiene, así que se conservan los entitlements de otros plugins.
  • Android: se añaden reglas keep para el puente Java al proguard-unity.txt generado, de modo que una build de release minificada conserva los métodos que C# llama por su nombre.

Si las notificaciones nunca llegan en una build que por lo demás funciona, comprueba que la capacidad Push Notifications esté presente en el target: es el único ajuste cuya ausencia no produce ningún error en ninguna parte.

Suscripción de marketing​

Si se puede enviar mensajes a esta persona en un canal. Es un consentimiento distinto de OptOut(), que trata de si se la puede observar: ninguno implica el otro.

Esto es lo que escribe un centro de preferencias dentro de la app.

JoryioSDK.SetSubscription("email", "unsubscribed");
JoryioSDK.AddToSubscriptionGroup("list_abc123", "email");

Canales: email, sms, whatsapp, push, viber. Estados: optedIn, subscribed, unsubscribed.

Se requiere un identificador: la llamada se omite, con un error registrado, si no hay ni user id ni anonymous id.