Sintaxis de plantillas in-app nativas
Los mensajes in-app nativos se renderizan directamente por el SDK. No se utiliza ningún WebView. En lugar de una página index.html, el recurso ZIP contiene un archivo native-config.json que describe el mensaje como datos estructurados (tipo de diseño, textos, colores, imágenes, botones). El SDK lee este archivo y dibuja la vista nativa correspondiente, lo que proporciona animaciones más fluidas y un mejor rendimiento que una página web incrustada.
Esta guía documenta el esquema de native-config.json: campos, tipos y ejemplos para cada tipo de visualización. Para el formato clásico basado en HTML, consulta Sintaxis de plantillas de Rich Media.
Prerrequisitos
Anchor link toLos mensajes in-app nativos requieren:
- iOS: SDK 7.2.0 o posterior (7.2.1+ para banner, carrusel y hoja)
- Android: SDK 6.10.0 o posterior (6.10.1+ para banner, carrusel y hoja)
No todos los tipos de visualización están disponibles en ambas plataformas todavía. Consulta Soporte de plataforma antes de depender de un formato específico.
Soporte de plataforma
Anchor link to| Tipo de visualización | iOS | Android |
|---|---|---|
| modal | ✅ 7.2.0+ | ✅ 6.10.0+ |
| fullscreen | ✅ 7.2.0+ | ✅ 6.10.0+ |
| stories | ✅ 7.2.0+ | ✅ 6.10.0+ |
| banner | ✅ 7.2.1+ | ✅ 6.10.1+ |
| carousel | ✅ 7.2.1+ | ✅ 6.10.1+ |
| sheet | ✅ 7.2.1+ | ✅ 6.10.1+ |
| video | ✅ | Aún no disponible |
| pip | ✅ | Aún no disponible |
| scratchcard | ✅ | Aún no disponible |
| spinwheel | ✅ | Aún no disponible |
Estructura de la plantilla
Anchor link toUna plantilla in-app nativa es un archivo ZIP, igual que una plantilla de Rich Media normal, excepto que la raíz contiene un archivo native-config.json en lugar de index.html:
<template>.zip├── native-config.json ← requerido, diseño y contenido├── pushwoosh.json ← opcional, localización (ver abajo)Las imágenes y videos referenciados desde native-config.json (image, poster, fallback, url en pip/video) deben ser URLs HTTPS absolutas. El SDK los carga a través de la red. No lee archivos locales del archivo.
La configuración en sí es un único objeto JSON:
{ "displayType": "<type>", "<type>": { /* bloque de contenido para este tipo */ } }displayType selecciona uno de los diez formatos a continuación. El objeto bajo la clave correspondiente contiene el contenido de ese formato. Una configuración con un displayType desconocido, un bloque de contenido faltante o una lista requerida vacía (items para carousel/stories, segments para spinwheel) no es válida. El SDK omite mostrarla en lugar de renderizar un diseño roto.
Los ajustes de entrega (fechas de inicio/fin y limitación de frecuencia) no forman parte de native-config.json. Se configuran de la misma manera que para cualquier otro mensaje in-app, en el paso de Ajustes de visualización de la campaña.
La limitación de frecuencia necesita además una activación explícita por parte del SDK para tener efecto en los mensajes in-app nativos. Consulta Integración del SDK.
Colores
Anchor link toCada valor de color es una cadena hexadecimal de CSS: #RGB, #RGBA, #RRGGBB o #RRGGBBAA. El # inicial es obligatorio en las cuatro formas.
Bloques de construcción compartidos
Anchor link toEstos objetos más pequeños se reutilizan en varios tipos de visualización.
Texto
Anchor link to| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
text | string | sí | El contenido del texto |
color | string | sí | Color del texto |
{ "text": "Gira para obtener un beneficio de garaje", "color": "#FFFFFFFF" }Borde
Anchor link to| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
color | string | sí | Color del borde |
radius | number | sí | Radio de la esquina, en puntos |
{ "color": "#0E72E5FF", "radius": 12 }Portada
Anchor link toUna imagen opcional sobre un color de fondo. Usado por fullscreen y scratchcard.
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
image | string | no | URL de la imagen de portada |
background | string | sí | Color de fondo mostrado debajo (o en lugar de) la imagen |
{ "image": "https://example.com/cover.jpg", "background": "#1A1A1EFF" }Acción
Anchor link toUna unión discriminada en type:
| Variante | Campos | Descripción |
|---|---|---|
{ "type": "close" } | Ninguno | Cierra el mensaje in-app |
{ "type": "url", "url": string } | url requerido | Abre una URL o un enlace profundo |
{ "type": "url", "url": "pushwoosh://sale" }Botón
Anchor link to| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
text | Text | sí | Etiqueta del botón |
background | string | sí | Color de relleno del botón |
border | Border | sí | Borde del botón |
action | Action | sí | Acción que se dispara al tocar |
spinButton (spinwheel) y revealButton (scratchcard) usan la misma forma sin action. Su comportamiento (girar la rueda, revelar la tarjeta) está incorporado.
{ "text": { "text": "Reservar una prueba de manejo", "color": "#FFFFFFFF" }, "background": "#0E72E5FF", "border": { "color": "#0E72E5FF", "radius": 12 }, "action": { "type": "url", "url": "pushwoosh://sale" }}Premio
Anchor link toEl panel de premios mostrado por scratchcard y spinwheel. Un premio válido tiene un title o un code.
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
title | Text | no | Encabezado del premio |
message | Text | no | Descripción del premio |
code | string | no | Código promocional, renderizado con un botón de copiar |
button | Button | no | Botón de confirmación con su propia acción |
{ "title": { "text": "20% de descuento en detallado", "color": "#111111FF" }, "message": { "text": "Válido para cualquier reserva de detallado completo este mes.", "color": "#555555FF" }, "code": "APEX20", "button": { "text": { "text": "Reservar detallado", "color": "#FFFFFFFF" }, "background": "#B3227CFF", "border": { "color": "#B3227CFF", "radius": 12 }, "action": { "type": "url", "url": "pushwoosh://detailing" } }}Tipos de visualización
Anchor link tobanner
Anchor link toUna barra compacta anclada al borde superior o inferior de la pantalla.
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
showClose | boolean | sí | Mostrar un botón de cerrar (✕) |
position | top | bottom | sí | Borde de la pantalla |
background | string | sí | Color de fondo de la barra |
image | string | no | Miniatura a la izquierda |
title | Text | no | Título de una sola línea, truncado con puntos suspensivos |
message | Text | no | Texto del cuerpo, hasta 2 líneas |
action | Action | sí | Se dispara cuando se toca la propia barra |
autoDismiss | number | no | Se cierra automáticamente después de este número de segundos. Omitir para mantenerlo hasta que se cierre |
{ "displayType": "banner", "banner": { "showClose": true, "position": "bottom", "background": "#4B5057FF", "image": "https://example.com/thumb.jpg", "title": { "text": "El Alpine A110 acaba de llegar", "color": "#FFFFFFFF" }, "message": { "text": "El ícono peso pluma — toca para ver la configuración", "color": "#FFFFFFFF" }, "action": { "type": "url", "url": "pushwoosh://product/x6f" }, "autoDismiss": 6 }}carousel
Anchor link toUn conjunto de tarjetas a pantalla completa, deslizables, con puntos indicadores de página.
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
showClose | boolean | sí | Mostrar un botón de cerrar (✕) |
items | Item[] | sí | Tarjetas (al menos 1) |
Elemento del carrusel:
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
title | Text | no | Título de la tarjeta |
message | Text | no | Subtítulo de la tarjeta |
image | string | no | Imagen de la tarjeta |
action | Action | no | Se dispara cuando se toca la tarjeta |
{ "displayType": "carousel", "carousel": { "showClose": true, "items": [ { "image": "https://example.com/card-1.jpg", "title": { "text": "AMG GT R", "color": "#FFFFFFFF" }, "message": { "text": "V8 biturbo de 585 hp — recién llegado", "color": "#FFFFFFFF" }, "action": { "type": "url", "url": "pushwoosh://product/n6fx" } }, { "image": "https://example.com/card-2.jpg", "title": { "text": "Alpine A110", "color": "#FFFFFFFF" }, "message": { "text": "Ícono peso pluma — asignación limitada", "color": "#FFFFFFFF" }, "action": { "type": "url", "url": "pushwoosh://product/x6f" } } ] }}fullscreen
Anchor link toUna imagen de portada de borde a borde con texto y botones superpuestos.
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
showClose | boolean | sí | Mostrar un botón de cerrar (✕) |
cover | Cover | sí | Imagen y color de fondo |
title | Text | no | Título |
message | Text | no | Texto del cuerpo |
buttons | Button[] | sí | Botones en la parte inferior (puede estar vacío) |
{ "displayType": "fullscreen", "fullscreen": { "showClose": true, "cover": { "image": "https://example.com/hero.jpg", "background": "#1A1A1EFF" }, "title": { "text": "Puro Maranello", "color": "#FFFFFFFF" }, "message": { "text": "El cavallino rampante, reinventado.", "color": "#EBEBEBFF" }, "buttons": [ { "text": { "text": "Reservar ahora", "color": "#FFFFFFFF" }, "background": "#0E72E5FF", "border": { "color": "#0E72E5FF", "radius": 8 }, "action": { "type": "url", "url": "pushwoosh://sale" } }, { "text": { "text": "Ahora no", "color": "#FFFFFFFF" }, "background": "#00000000", "border": { "color": "#FFFFFF99", "radius": 8 }, "action": { "type": "close" } } ] }}modal
Anchor link toUna tarjeta centrada.
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
showClose | boolean | sí | Mostrar un botón de cerrar (✕) |
dimBackground | boolean | sí | Atenuar la pantalla detrás de la tarjeta |
background | string | sí | Color de fondo de la tarjeta |
image | string | no | Imagen de portada |
title | Text | no | Título |
message | Text | no | Texto del cuerpo |
buttons | Button[] | sí | Botones debajo del texto (puede estar vacío) |
{ "displayType": "modal", "modal": { "showClose": true, "dimBackground": true, "background": "#FFFFFFFF", "image": "https://example.com/cover.jpg", "title": { "text": "El GT R ha llegado", "color": "#4B5057FF" }, "message": { "text": "585 hp — ahora en la sala de exposición.", "color": "#4B5057FF" }, "buttons": [ { "text": { "text": "Reservar una prueba de manejo", "color": "#FFFFFFFF" }, "background": "#0E72E5FF", "border": { "color": "#0E72E5FF", "radius": 12 }, "action": { "type": "url", "url": "pushwoosh://sale" } }, { "text": { "text": "Ahora no", "color": "#4B5057FF" }, "background": "#FFFFFFFF", "border": { "color": "#4B5057FF", "radius": 12 }, "action": { "type": "close" } } ] }}Una ventana de video flotante picture-in-picture anclada a una esquina de la pantalla.
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
showClose | boolean | sí | Mostrar un botón de cerrar (✕) |
position | bottom-right | bottom-left | top-right | top-left | sí | Esquina de la pantalla |
loop | boolean | sí | Repetir la reproducción |
muted | boolean | sí | Empezar silenciado |
url | string | sí | URL del video |
poster | string | no | Póster mostrado antes de que comience la reproducción |
fallback | string | no | Imagen mostrada si el video no se puede reproducir |
width | number | sí | Ancho de la ventana como porcentaje del ancho de la pantalla, limitado a 15–70 |
aspectRatio | number | sí | Relación alto/ancho de la ventana |
borderRadius | number | no | Radio de la esquina de la ventana, en puntos |
action | Action | no | Se dispara cuando se toca la propia ventana |
No hay botones configurables en pip. Los controles de la ventana (expandir a pantalla completa, silenciar, cerrar) son proporcionados por el sistema.
{ "displayType": "pip", "pip": { "showClose": true, "position": "bottom-right", "loop": true, "muted": true, "url": "https://example.com/teaser.mp4", "poster": "https://example.com/poster.jpg", "width": 40, "aspectRatio": 0.5625, "action": { "type": "url", "url": "pushwoosh://product/x6f" } }}scratchcard
Anchor link toUna tarjeta con el premio oculto bajo una capa de lámina rascable.
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
showClose | boolean | sí | Mostrar un botón de cerrar (✕) |
background | string | string[] | sí | Color de fondo de la tarjeta, o paradas de gradiente |
revealThreshold | number | sí | Fracción de la lámina que debe rascarse (0–1) antes de que se revele el premio |
cover | Cover | sí | La capa de lámina. Sin una image, se muestra una pista de “rasca aquí” sobre el color de fondo |
revealButton | Button (no action) | no | Botón “Revelar instantáneamente” |
title | Text | no | Título |
message | Text | no | Texto del cuerpo |
reward | Reward | sí | El premio oculto bajo la lámina |
{ "displayType": "scratchcard", "scratchcard": { "showClose": true, "background": ["#3A1C71FF", "#B3227CFF", "#E0503AFF"], "revealThreshold": 0.55, "cover": { "background": "#C9CDD6FF" }, "revealButton": { "text": { "text": "Revelar sin rascar", "color": "#3A1C71FF" }, "background": "#F2DFF5FF", "border": { "color": "#F2DFF5FF", "radius": 10 } }, "title": { "text": "Tu premio de lealtad", "color": "#FFFFFFFF" }, "message": { "text": "Rasca la lámina para revelar el beneficio de garaje de esta semana.", "color": "#F2DFF5FF" }, "reward": { "title": { "text": "20% de descuento en detallado", "color": "#111111FF" }, "message": { "text": "Válido para cualquier reserva de detallado completo este mes.", "color": "#555555FF" }, "code": "APEX20", "button": { "text": { "text": "Reservar detallado", "color": "#FFFFFFFF" }, "background": "#B3227CFF", "border": { "color": "#B3227CFF", "radius": 12 }, "action": { "type": "url", "url": "pushwoosh://detailing" } } } }}sheet
Anchor link toUna tarjeta fijada al borde inferior, con un asa para arrastrar.
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
showClose | boolean | sí | Mostrar un botón de cerrar (✕) |
dimBackground | boolean | sí | Atenuar la pantalla detrás de la hoja |
background | string | sí | Color de fondo de la hoja |
image | string | no | Imagen de portada |
title | Text | no | Título |
message | Text | no | Texto del cuerpo |
buttons | Button[] | sí | Botones debajo del texto (puede estar vacío) |
{ "displayType": "sheet", "sheet": { "showClose": true, "dimBackground": true, "background": "#FFFFFFFF", "image": "https://example.com/cover.jpg", "title": { "text": "Tu cotización está lista", "color": "#000000FF" }, "message": { "text": "Compra garantizada para tu A110: $68,500.", "color": "#000000FF" }, "buttons": [ { "text": { "text": "Obtener cotización garantizada", "color": "#FFFFFFFF" }, "background": "#0E72E5FF", "border": { "color": "#0E72E5FF", "radius": 12 }, "action": { "type": "url", "url": "pushwoosh://sale" } } ] }}spinwheel
Anchor link toUna rueda de la fortuna con segmentos ponderados y un botón central.
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
showClose | boolean | sí | Mostrar un botón de cerrar (✕) |
background | string | string[] | sí | Color de fondo de la tarjeta, o paradas de gradiente |
winIndex | number | sí | Índice (base 0) del segmento ganador |
spinButton | Button (no action) | sí | Botón central |
title | Text | no | Título |
message | Text | no | Texto del cuerpo |
reward | Reward | sí | Premio por el giro ganador (alternativa para segmentos sin el suyo propio) |
loseTitle | Text | no | Encabezado mostrado en una pérdida |
segments | Segment[] | sí | Segmentos de la rueda (el SDK espera 2–12) |
Segmento:
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
message | Text | sí | Etiqueta del segmento |
color | string | no | Color del segmento. Omitir para una paleta alternativa aplicada alrededor de la rueda |
weight | number | sí | Tamaño relativo del segmento |
reward | Reward | no | Premio específico del segmento |
{ "displayType": "spinwheel", "spinwheel": { "showClose": true, "background": ["#1B1B46FF", "#5B2B8FFF", "#B0338AFF"], "winIndex": 1, "spinButton": { "text": { "text": "GIRAR", "color": "#1B1B46FF" }, "background": "#F2C94CFF", "border": { "color": "#D9A02BFF", "radius": 36 } }, "title": { "text": "Gira para obtener un beneficio de garaje", "color": "#FFFFFFFF" }, "message": { "text": "Un giro — cada porción gana esta semana.", "color": "#E3D9F2FF" }, "reward": { "title": { "text": "¡Ganaste un beneficio de garaje!", "color": "#FFFFFFFF" }, "code": "APEXPERK", "button": { "text": { "text": "Reclamar", "color": "#FFFFFFFF" }, "background": "#5B2B8FFF", "border": { "color": "#5B2B8FFF", "radius": 12 }, "action": { "type": "close" } } }, "segments": [ { "message": { "text": "5% de descuento", "color": "#FFFFFFFF" }, "color": "#5856D6FF", "weight": 1 }, { "message": { "text": "20% de descuento", "color": "#FFFFFFFF" }, "color": "#30B0C7FF", "weight": 1, "reward": { "title": { "text": "20% de descuento en tu próximo servicio", "color": "#FFFFFFFF" }, "code": "SPIN20", "button": { "text": { "text": "Reclamar oferta de servicio", "color": "#FFFFFFFF" }, "background": "#30B0C7FF", "border": { "color": "#30B0C7FF", "radius": 12 }, "action": { "type": "url", "url": "pushwoosh://service" } } } }, { "message": { "text": "Lavado gratis", "color": "#FFFFFFFF" }, "color": "#FF2D55FF", "weight": 1 } ] }}stories
Anchor link toDiapositivas a pantalla completa con barras de progreso en la parte superior, similar a las historias de las redes sociales.
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
showClose | boolean | sí | Mostrar un botón de cerrar (✕) |
loop | boolean | sí | Reiniciar desde la primera diapositiva después de la última |
items | Item[] | sí | Diapositivas (al menos 1) |
Elemento de las historias:
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
title | Text | no | Título |
message | Text | no | Subtítulo |
image | string | no | Imagen de fondo de la diapositiva |
buttons | Button[] | sí | Botones de llamada a la acción en la parte inferior (puede estar vacío) |
duration | number | sí | Duración de la diapositiva, en segundos |
{ "displayType": "stories", "stories": { "showClose": true, "loop": false, "items": [ { "image": "https://example.com/slide-1.jpg", "title": { "text": "AMG GT R", "color": "#FFFFFFFF" }, "message": { "text": "El especial del Infierno Verde", "color": "#FFFFFFFF" }, "buttons": [ { "text": { "text": "Configura el tuyo", "color": "#FFFFFFFF" }, "background": "#0F0F0FFF", "border": { "color": "#0F0F0FFF", "radius": 26 }, "action": { "type": "url", "url": "pushwoosh://product/n6fx" } } ], "duration": 4 }, { "image": "https://example.com/slide-2.jpg", "title": { "text": "Alpine A110", "color": "#FFFFFFFF" }, "message": { "text": "La leyenda peso pluma, renacida", "color": "#FFFFFFFF" }, "buttons": [], "duration": 4 } ] }}video
Anchor link toVideo a pantalla completa con texto y botones superpuestos.
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
showClose | boolean | sí | Mostrar un botón de cerrar (✕) |
loop | boolean | sí | Repetir la reproducción |
muted | boolean | sí | Empezar silenciado |
url | string | sí | URL del video (HLS o MP4) |
poster | string | no | Póster mostrado antes de que comience la reproducción |
fallback | string | no | Imagen mostrada si el video no se puede reproducir |
title | Text | no | Título |
message | Text | no | Texto del cuerpo |
buttons | Button[] | sí | Botones de llamada a la acción en la parte inferior (puede estar vacío) |
{ "displayType": "video", "video": { "showClose": true, "loop": true, "muted": true, "url": "https://example.com/reveal.mp4", "poster": "https://example.com/poster.jpg", "title": { "text": "La revelación", "color": "#FFFFFFFF" }, "message": { "text": "Míralo moverse antes que nadie.", "color": "#EBEBEBFF" }, "buttons": [ { "text": { "text": "Comprar la línea", "color": "#FFFFFFFF" }, "background": "#0E72E5FF", "border": { "color": "#0E72E5FF", "radius": 14 }, "action": { "type": "url", "url": "pushwoosh://sale" } } ] }}Localización
Anchor link toLos mensajes in-app nativos reutilizan el mismo mecanismo de localización que los Rich Media HTML: los valores de cadena en native-config.json pueden contener marcadores de posición {{key|type|default}}, y las traducciones se encuentran en un archivo pushwoosh.json junto a él, en el mismo formato descrito en Añadir pushwoosh.json. Un marcador de posición puede aparecer en cualquier campo de cadena, a cualquier profundidad (un título, una etiqueta de botón, una URL de imagen, una URL de acción).
Integración del SDK
Anchor link toUna vez que añades el módulo del SDK de in-app nativos a tu aplicación, los mensajes se muestran automáticamente. No se requiere código adicional para mostrar mensajes activados por un push, un Customer Journey, postEvent o la bandeja de entrada.
El SDK también expone una pequeña API para el control manual:
- iOS:
Pushwoosh.inApp(móduloPushwooshInApp) - Android:
PushwooshInAppUi(módulopushwoosh-inapp-ui)
| Capacidad | iOS | Android |
|---|---|---|
| Mostrar una configuración directamente (pruebas/uso manual) | Pushwoosh.inApp.present(config) | PushwooshInAppUi.present(configJson) |
| Observar el ciclo de vida y los clics | delegate (PWInAppMessageDelegate) | delegate (InAppMessageDelegate) |
| Comprobar si hay algo en pantalla | isPresenting | isPresenting |
| Descartar lo que se esté mostrando actualmente | dismiss() | dismiss() |
| Pausar / reanudar la visualización | isPaused | isPaused |
Aplicar la limitación de maxDisplays / cooldown | setFrequencyCapEnabled(_:) | setFrequencyCapEnabled(...) |
Callbacks del delegado (todos se ejecutan en el hilo principal): shouldDisplay (devuelve false para suprimir un mensaje antes de que se muestre, por ejemplo, en una pantalla de pago), willPresent, didPresent, didClose y clickedAction (se ejecuta cuando el usuario toca una acción de url, antes de que se abra la URL).
iOS informa adicionalmente de rewardRevealed y rewardClaimed para las plantillas gamificadas scratchcard y spinwheel.
// iOSPushwoosh.inApp.delegate = selfPushwoosh.inApp.setFrequencyCapEnabled(true)// AndroidPushwooshInAppUi.delegate = thisPushwooshInAppUi.setFrequencyCapEnabled(true)