Recientemente tuve la suerte de trabajar en un proyecto de implementación de analítica en apps móviles. Se trataba de implementar Firebase Mobile Remote Commands a través de Tealium iQ en una app, tanto en iOS como en Android. El objetivo era enviar llamadas de tracking personalizadas a Firebase directamente desde Tealium iQ. A la hora de configurar los data mappings en el tag de Remote Commands me topé con una casuística que nunca había visto antes. En lugar de asignar a un parámetro concreto un valor determinado, tuve que mapear todos los parámetros por un lado y todos los valores por otro. Se me ocurrió que sería buena idea escribir un post al respecto por si acaso te encuentras con esta misma situación en algún momento.
¿Asignar en un tag los parámetros por un lado y sus valores por otro?
Aunque Firebase Mobile Remote Commands es el punto de partida de este post, no voy a entrar a explicar en detalle qué es ni cómo funciona, pero si te daré unas breves pinceladas a respecto. Firebase Remote Commands es un módulo con el que puedes añadir una serie de librerías de Firebase a tu app para, entre otras cosas, enviar a Firebase llamadas de tracking desde Tealium iQ. De lo contrario, tendrías que implementar estas llamadas directamente en el SDK de Firebase implementado en tu app. Tienes dos formas de proceder con esta implementación: a través de un archivo JSON implementado en el SDK de tu aplicación, o a través de la etiqueta de Firebase Mobile Remote Commands de Tealium iQ. En el proyecto que he mencionado al comienzo de este post seguí este segundo approach.
Añadiendo los data mappings correspondientes a este tag, me topé con una casuística súper concreta que me hizo pensar. A la hora de mapear los userProperties de cada evento personalizado que se iba a enviar a Firebase, el tag de Remote Commands me pedía el nombre de todas las propiedades de usuario por un lado, y su valor por otro (en otro mapping diferente). En seguida me surgió la duda de cómo asociar cada uno de estos pares clave-valor. Pregunté a mis colegas de profesión en Measure Slack, y me ayudaron a entender el asunto. Estos campos admiten como valor o bien un string o un array, por lo que la solución a este rompecabezas pasaba por generar dos arrays: uno para los nombres de los userProperties, y otro para los valores. Hecho el mapeo, el código de la etiqueta se encarga de asociar todos los pares clave-valor.
Una extensión procesa la llamada de tracking y lleva los dos arrays al data layer (objeto b de Tealium iQ)
Hasta aquí todo bien pero, ¿dónde se exponen los valores con los que se van a generar estos arrays? Pues en las llamadas de tracking que se envíen a Tealium iQ (utag.view() o utag.link() en el caso de una implementación web) Es decir, se trata de trabajar con un objeto javascript (incluido en la llamada de tracking) para generar un array con sus claves y otro con sus valores.
Probablemente haya muchas maneras de hacerlo, pero yo creo que la mejor es a través de una extensión de Tealium iQ de tipo Javascript o Advanced Javascript que se encargue de:
- Procesar la llamada de tracking que recibe Tealium
- Generar dos arrays con sus respectivas claves y valores
- Llevar estos arrays al objeto b para poder recoger cada uno en una variable del data layer de Tealium iQ y efectuar el mapping en cualquier etiqueta
Los métodos Object.keys(), Object.values() y Object.entries()
Toca entrar en materia. ¿Cómo se puede generar un array partiendo de las claves y valores de un objeto? Como siempre te digo, probablemente haya muchas formas diferentes de hacerlo, pero yo te propongo usar los siguiente tres métodos
- Object.keys() Devuelve un array con las claves de un objeto
- Object.values() Devuelve un array con los valores de un objeto
- Object.entries() Devuelve un array de arrays, cada uno de los cuales contiene una clave y su valor correspondiente
Supón el siguiente objeto:
let testObject = {
key1:'value1',
key2:'value2',
key3:'value3',
key4:'value4',
key5:'value5',
key6:'value6',
key7:'value7',
key8:'value8',
key9:'value9',
key10:'value10'
}
Object.keys()
y Object.values()
puedes generar dos arrays, uno con las claves del objeto, y otro con sus valores. Siendo así, podrías probar con el siguiente código, fíjate: let testObject = {
key1:'value1',
key2:'value2',
key3:'value3',
key4:'value4',
key5:'value5',
key6:'value6',
key7:'value7',
key8:'value8',
key9:'value9',
key10:'value10'
};
let testArray = Object.keys(testObject);
let valuesArray = Object.values(testObject);
Object.entries()
, aunque aquí el desarrollo cambia, ya que el método Object.entries()
devuelve un array de arrays, conteniendo cada uno de estos un par clave-valor del objeto. Mira, fíjate, lo vas a ver súper bien en el siguiente pantallazo: Teniendo ya este array con todos los valores del objeto, habría que generar dos nuevos arrays: uno con las claves y otro con los valores. Una forma de hacer esto sería con el método forEach(), que permite ejecutar una misma función sobre todo los elementos de un array. Puedes probar con el siguiente código:
let testObject = {
key1:'value1',
key2:'value2',
key3:'value3',
key4:'value4',
key5:'value5',
key6:'value6',
key7:'value7',
key8:'value8',
key9:'value9',
key10:'value10'
};
//Initialize empty keys array
let keysArray = [];
//Initialize empty values array
let valuesArray = [];
//Initialize array with values and keys with Objetc.entries() method
let keysValuesArray = Object.entries(testObject);
//Call .forEach() method to populated keysArrat and valuesArray
keysValuesArray.forEach(function(element){
keysArray.push(element[0]);
valuesArray.push(element[1]);
});
¿Lo ves? El resultado final es el mismo. Tendrías dos arrays: uno con las claves del objeto y otro con los valores. Llevados al objeto b de Tealium iQ ya podrías usarlos en el mapping de cualquier tag.
¿Cómo desarrollar esta lógica en Tealium iQ?
Como te he adelantado un poco más arriba, bajo mi punto de vista la mejor forma de desarrollar esta lógica en Tealium es a través de una extensión Javascript o Advanced Javascript. No te puedo mostrar esto en la práctica por no tener un acceso a Tealium iQ con el que poder hacerlo, pero sí te puedo ilustrar el ejercicio añadiendo el desarrollo al metódo utag.link()
(es un enfoque similar al que planteo en el post Tealium iQ debugging snippet: audita el payload de las llamadas utag.view() y utag.link(), pero aplicado a otra casuística). Al fin y al cabo es un método global, por lo que se puede acceder a él desde el objeto window
de tu navegador.
Fíjate en el siguiente código:
(function () {
let utagLinkArrayGenerator = function(a, c, d) {
//Create arrayKeys and arrayValues from payload of utag.link() call
let arrayKeys = Object.keys(a);
let arrayValues = Object.values(a);
//Expose new values in utag_data object
utag_data.arrayKeys = arrayKeys;
utag_data.arrayValues = arrayValues;
//Original, unmodified code of utag.link()
return this.track({
event: 'link',
data: a || {},
cfg: {
cb: c,
uids: d
}
})
};
utag.link = utagLinkArrayGenerator;
})();
Si lo ejecutas en la consola javascript de un site en el que se haya implementado Tealium iQ, en esencia estás consiguiendo el mismo resultado que con la extensión que te planteo en este post: cada vez que haya una llamada utag.link()
(uso este método a modo de ejemplo, se podría hacer lo mismo con utag.view()
) se generarán dos arrays partiendo del objeto de la llamada (uno con las claves y otro con los valores) y se expondrán en el data layer, en este caso en el objeto utag_data
.
En los siguientes pantallazos puedes ver esto en la práctica:
utag
, y es un contexto global porque el objeto utag
se constituye como una propiedad del objeto window
. Esto es muy importante que lo entiendas, ya que para desarrollar esta lógica en una extensión vas a trabajar en el contexto de Tealium iQ, es decir, en el de la librería utag.js
. En otras palabras: vas a trabajar en un scope diferente. Siendo así, puedes probar con el siguiente código en tu extensión, asignándole un scope de before load rules, por ejemplo (nunca publiques nada en PROD sin antes haberlo probado en otro entorno anterior, como por ejemplo DEV): try{
if (a === 'link'){
//Create arrayOfKeys and arrayOfValues from payload of utag.link() call - b object
let arrayOfKeys = Object.keys(b);
let arrayOfValues = Object.values(b);
//Expose new values in b object - local reference of utag.data object
b.arrayOfKeys = arrayOfKeys;
b.arrayOfValues = arrayOfValues;
}
}
catch(error){}
Como ves, en el código se referecia al objeto b, que es una copia del objeto utag.data
en el contexto local de la librería utag.js
. Trabajar con el objeto b y saber cuándo referenciarlo tiene su miga, no te lo negaré. Te recomiendo que consultes la documentación de Tealium iQ al respecto. Como siempre te digo, la documentación de Tealium es maravillosa.
Nota final: ¿Javascript en una app nativa?
En este post hago referencia a una implementación de Firebase Mobile Remote Commands en unas apps nativas en la que tuve oportunidad de trabajar ¿Cómo es posible entonces hablar de javascript en el contexto de una app nativa? Bajo mi punto de vista, aquí reside una de las ventajas de Tealium iQ frente a otros tag managers: es muy flexible a la hora de trabajar en apps móviles.
Habiendo implementado el SDK de Tealium iQ en la app, el profile genera un webview sobre la aplicación, haciendo posible servir la librería utag.js
e interactuar con ella con javascript. Recuerda, no obstante, que podrías encontrarte con esta misma casuística (tener que mapear en un tag claves y valores en arrays diferentes) en una implementación web estándar.