Sintaxe de modelos de in-app nativos
In-apps nativos são renderizados diretamente pelo SDK. Nenhum WebView está envolvido. Em vez de uma página index.html, o recurso ZIP carrega um arquivo native-config.json que descreve a mensagem como dados estruturados (tipo de layout, textos, cores, imagens, botões). O SDK lê este arquivo e desenha a visualização nativa correspondente, o que proporciona animações mais suaves e melhor desempenho do que uma página web incorporada.
Este guia documenta o esquema native-config.json: campos, tipos e exemplos para cada tipo de exibição. Para o formato clássico baseado em HTML, consulte Sintaxe de modelos de Rich Media.
Pré-requisitos
Anchor link toIn-apps nativos exigem:
- iOS: SDK 7.2.0 ou posterior (7.2.1+ para banner, carrossel e folha)
- Android: SDK 6.10.0 ou posterior (6.10.1+ para banner, carrossel e folha)
Nem todo tipo de exibição está disponível em ambas as plataformas ainda. Verifique o Suporte de plataforma antes de depender de um formato específico.
Suporte de plataforma
Anchor link to| Tipo de exibição | 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 | ✅ | ✅ 6.11.0+ |
| pip | ✅ | Ainda não disponível |
| scratchcard | ✅ | Ainda não disponível |
| spinwheel | ✅ | Ainda não disponível |
Estrutura do modelo
Anchor link toUm modelo de in-app nativo é um arquivo ZIP, igual a um modelo de Rich Media regular, exceto que a raiz contém um arquivo native-config.json em vez de index.html:
<template>.zip├── native-config.json ← obrigatório, layout e conteúdo├── pushwoosh.json ← opcional, localização (veja abaixo)Imagens e vídeos referenciados de native-config.json (image, poster, fallback, url em pip/video) devem ser URLs HTTPS absolutas. O SDK os carrega pela rede. Ele não lê arquivos locais do arquivo ZIP.
A configuração em si é um único objeto JSON:
{ "displayType": "<type>", "<type>": { /* bloco de conteúdo para este tipo */ } }displayType seleciona um dos dez formatos abaixo. O objeto sob a chave correspondente contém o conteúdo desse formato. Uma configuração com um displayType desconhecido, um bloco de conteúdo ausente ou uma lista obrigatória vazia (items para carousel/stories, segments para spinwheel) é inválida. O SDK ignora a exibição em vez de renderizar um layout quebrado.
As configurações de entrega (datas de início/fim e limitação de frequência) não fazem parte do native-config.json. Elas são configuradas da mesma forma que para qualquer outro in-app, na etapa de Configurações de exibição da campanha.
A limitação de frequência adicionalmente precisa de um opt-in explícito do lado do SDK para ter efeito em in-apps nativos. Consulte Integração do SDK.
Cores
Anchor link toCada valor de cor é uma string hexadecimal CSS: #RGB, #RGBA, #RRGGBB ou #RRGGBBAA. O # inicial é obrigatório em todas as quatro formas.
Blocos de construção compartilhados
Anchor link toEsses objetos menores são reutilizados em vários tipos de exibição.
Texto
Anchor link to| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
text | string | sim | O conteúdo do texto |
color | string | sim | Cor do texto |
{ "text": "Gire para um benefício de garagem", "color": "#FFFFFFFF" }Borda
Anchor link to| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
color | string | sim | Cor da borda |
radius | number | sim | Raio do canto, em pontos |
{ "color": "#0E72E5FF", "radius": 12 }Capa
Anchor link toUma imagem opcional sobre uma cor de fundo. Usado por fullscreen e scratchcard.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
image | string | não | URL da imagem de capa |
background | string | sim | Cor de fundo mostrada sob (ou em vez de) a imagem |
{ "image": "https://example.com/cover.jpg", "background": "#1A1A1EFF" }Ação
Anchor link toUma união discriminada em type:
| Variante | Campos | Descrição |
|---|---|---|
{ "type": "close" } | Nenhum | Fecha o in-app |
{ "type": "url", "url": string } | url obrigatório | Abre uma URL ou deep link |
{ "type": "url", "url": "pushwoosh://sale" }Botão
Anchor link to| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
text | Text | sim | Rótulo do botão |
background | string | sim | Cor de preenchimento do botão |
border | Border | sim | Borda do botão |
action | Action | sim | Ação disparada ao tocar |
spinButton (spinwheel) e revealButton (scratchcard) usam a mesma forma sem action. Seu comportamento (girar a roda, revelar o cartão) é embutido.
{ "text": { "text": "Agendar um test drive", "color": "#FFFFFFFF" }, "background": "#0E72E5FF", "border": { "color": "#0E72E5FF", "radius": 12 }, "action": { "type": "url", "url": "pushwoosh://sale" }}Recompensa
Anchor link toO painel de prêmios mostrado por scratchcard e spinwheel. Uma recompensa válida tem um title ou um code.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
title | Text | não | Título da recompensa |
message | Text | não | Descrição da recompensa |
code | string | não | Código promocional, renderizado com um botão de copiar |
button | Button | não | Botão de confirmação com sua própria ação |
{ "title": { "text": "20% de desconto em detalhamento", "color": "#111111FF" }, "message": { "text": "Válido para qualquer agendamento de detalhamento completo este mês.", "color": "#555555FF" }, "code": "APEX20", "button": { "text": { "text": "Agendar detalhamento", "color": "#FFFFFFFF" }, "background": "#B3227CFF", "border": { "color": "#B3227CFF", "radius": 12 }, "action": { "type": "url", "url": "pushwoosh://detailing" } }}Tipos de exibição
Anchor link tobanner
Anchor link toUma barra compacta ancorada na borda superior ou inferior da tela.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
showClose | boolean | sim | Mostrar um botão de fechar (✕) |
position | top | bottom | sim | Borda da tela |
background | string | sim | Cor de fundo da barra |
image | string | não | Miniatura à esquerda |
title | Text | não | Título de uma linha, truncado com reticências |
message | Text | não | Texto do corpo, até 2 linhas |
action | Action | sim | Disparado quando a própria barra é tocada |
autoDismiss | number | não | Fechar automaticamente após este número de segundos. Omita para mantê-lo até ser fechado |
{ "displayType": "banner", "banner": { "showClose": true, "position": "bottom", "background": "#4B5057FF", "image": "https://example.com/thumb.jpg", "title": { "text": "Alpine A110 acabou de chegar", "color": "#FFFFFFFF" }, "message": { "text": "O ícone peso-pena — toque para ver a montagem", "color": "#FFFFFFFF" }, "action": { "type": "url", "url": "pushwoosh://product/x6f" }, "autoDismiss": 6 }}carousel
Anchor link toUm conjunto de cartões de tela cheia, deslizáveis, com pontos indicadores de página.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
showClose | boolean | sim | Mostrar um botão de fechar (✕) |
items | Item[] | sim | Cartões (pelo menos 1) |
Item do carrossel:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
title | Text | não | Título do cartão |
message | Text | não | Subtítulo do cartão |
image | string | não | Imagem do cartão |
action | Action | não | Disparado quando o cartão é tocado |
{ "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 cv — acabou de chegar", "color": "#FFFFFFFF" }, "action": { "type": "url", "url": "pushwoosh://product/n6fx" } }, { "image": "https://example.com/card-2.jpg", "title": { "text": "Alpine A110", "color": "#FFFFFFFF" }, "message": { "text": "Ícone peso-pena — alocação limitada", "color": "#FFFFFFFF" }, "action": { "type": "url", "url": "pushwoosh://product/x6f" } } ] }}fullscreen
Anchor link toUma imagem de capa de ponta a ponta com texto e botões por cima.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
showClose | boolean | sim | Mostrar um botão de fechar (✕) |
cover | Cover | sim | Imagem e cor de fundo |
title | Text | não | Título |
message | Text | não | Texto do corpo |
buttons | Button[] | sim | Botões na parte inferior (pode estar vazio) |
{ "displayType": "fullscreen", "fullscreen": { "showClose": true, "cover": { "image": "https://example.com/hero.jpg", "background": "#1A1A1EFF" }, "title": { "text": "Puro Maranello", "color": "#FFFFFFFF" }, "message": { "text": "O cavalo empinado, reimaginado.", "color": "#EBEBEBFF" }, "buttons": [ { "text": { "text": "Reserve agora", "color": "#FFFFFFFF" }, "background": "#0E72E5FF", "border": { "color": "#0E72E5FF", "radius": 8 }, "action": { "type": "url", "url": "pushwoosh://sale" } }, { "text": { "text": "Agora não", "color": "#FFFFFFFF" }, "background": "#00000000", "border": { "color": "#FFFFFF99", "radius": 8 }, "action": { "type": "close" } } ] }}modal
Anchor link toUm cartão centralizado.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
showClose | boolean | sim | Mostrar um botão de fechar (✕) |
dimBackground | boolean | sim | Escurecer a tela atrás do cartão |
background | string | sim | Cor de fundo do cartão |
image | string | não | Imagem de capa |
title | Text | não | Título |
message | Text | não | Texto do corpo |
buttons | Button[] | sim | Botões abaixo do texto (pode estar vazio) |
{ "displayType": "modal", "modal": { "showClose": true, "dimBackground": true, "background": "#FFFFFFFF", "image": "https://example.com/cover.jpg", "title": { "text": "O GT R chegou", "color": "#4B5057FF" }, "message": { "text": "585 cv — agora no showroom.", "color": "#4B5057FF" }, "buttons": [ { "text": { "text": "Agendar um test drive", "color": "#FFFFFFFF" }, "background": "#0E72E5FF", "border": { "color": "#0E72E5FF", "radius": 12 }, "action": { "type": "url", "url": "pushwoosh://sale" } }, { "text": { "text": "Agora não", "color": "#4B5057FF" }, "background": "#FFFFFFFF", "border": { "color": "#4B5057FF", "radius": 12 }, "action": { "type": "close" } } ] }}Uma janela de vídeo picture-in-picture flutuante ancorada em um canto da tela.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
showClose | boolean | sim | Mostrar um botão de fechar (✕) |
position | bottom-right | bottom-left | top-right | top-left | sim | Canto da tela |
loop | boolean | sim | Repetir a reprodução |
muted | boolean | sim | Iniciar sem som |
url | string | sim | URL do vídeo |
poster | string | não | Pôster mostrado antes do início da reprodução |
fallback | string | não | Imagem mostrada se o vídeo falhar ao reproduzir |
width | number | sim | Largura da janela como porcentagem da largura da tela, limitada entre 15–70 |
aspectRatio | number | sim | Proporção altura/largura da janela |
borderRadius | number | não | Raio do canto da janela, em pontos |
action | Action | não | Disparado quando a própria janela é tocada |
Não há botões configuráveis em pip. Os controles da janela (expandir para tela cheia, silenciar, fechar) são fornecidos pelo 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 toUm cartão com a recompensa escondida sob uma camada de papel alumínio raspável.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
showClose | boolean | sim | Mostrar um botão de fechar (✕) |
background | string | string[] | sim | Cor de fundo do cartão, ou paradas de gradiente |
revealThreshold | number | sim | Fração do papel alumínio que deve ser raspada (0–1) antes que a recompensa seja revelada |
cover | Cover | sim | A camada de papel alumínio. Sem uma image, uma dica “raspe aqui” é mostrada na cor de fundo |
revealButton | Button (sem action) | não | Botão “Revelar instantaneamente” |
title | Text | não | Título |
message | Text | não | Texto do corpo |
reward | Reward | sim | O prêmio escondido sob o papel alumínio |
{ "displayType": "scratchcard", "scratchcard": { "showClose": true, "background": ["#3A1C71FF", "#B3227CFF", "#E0503AFF"], "revealThreshold": 0.55, "cover": { "background": "#C9CDD6FF" }, "revealButton": { "text": { "text": "Revelar sem raspar", "color": "#3A1C71FF" }, "background": "#F2DFF5FF", "border": { "color": "#F2DFF5FF", "radius": 10 } }, "title": { "text": "Sua recompensa de fidelidade", "color": "#FFFFFFFF" }, "message": { "text": "Raspe o papel alumínio para revelar o benefício de garagem desta semana.", "color": "#F2DFF5FF" }, "reward": { "title": { "text": "20% de desconto em detalhamento", "color": "#111111FF" }, "message": { "text": "Válido para qualquer agendamento de detalhamento completo este mês.", "color": "#555555FF" }, "code": "APEX20", "button": { "text": { "text": "Agendar detalhamento", "color": "#FFFFFFFF" }, "background": "#B3227CFF", "border": { "color": "#B3227CFF", "radius": 12 }, "action": { "type": "url", "url": "pushwoosh://detailing" } } } }}sheet
Anchor link toUm cartão fixado na borda inferior, com uma alça de arrasto.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
showClose | boolean | sim | Mostrar um botão de fechar (✕) |
dimBackground | boolean | sim | Escurecer a tela atrás da folha |
background | string | sim | Cor de fundo da folha |
image | string | não | Imagem de capa |
title | Text | não | Título |
message | Text | não | Texto do corpo |
buttons | Button[] | sim | Botões abaixo do texto (pode estar vazio) |
{ "displayType": "sheet", "sheet": { "showClose": true, "dimBackground": true, "background": "#FFFFFFFF", "image": "https://example.com/cover.jpg", "title": { "text": "Sua cotação está pronta", "color": "#000000FF" }, "message": { "text": "Compra garantida para seu A110: $68,500.", "color": "#000000FF" }, "buttons": [ { "text": { "text": "Obter cotação garantida", "color": "#FFFFFFFF" }, "background": "#0E72E5FF", "border": { "color": "#0E72E5FF", "radius": 12 }, "action": { "type": "url", "url": "pushwoosh://sale" } } ] }}spinwheel
Anchor link toUma roda da fortuna com segmentos ponderados e um botão central.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
showClose | boolean | sim | Mostrar um botão de fechar (✕) |
background | string | string[] | sim | Cor de fundo do cartão, ou paradas de gradiente |
winIndex | number | sim | Índice (base 0) do segmento vencedor |
spinButton | Button (sem action) | sim | Botão central |
title | Text | não | Título |
message | Text | não | Texto do corpo |
reward | Reward | sim | Recompensa pela rodada vencedora (fallback para segmentos sem a sua própria) |
loseTitle | Text | não | Título mostrado em uma perda |
segments | Segment[] | sim | Segmentos da roda (SDK espera 2–12) |
Segmento:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
message | Text | sim | Rótulo do segmento |
color | string | não | Cor do segmento. Omita para uma paleta de fallback aplicada ao redor da roda |
weight | number | sim | Tamanho relativo do segmento |
reward | Reward | não | Recompensa específica do 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": "Gire para um benefício de garagem", "color": "#FFFFFFFF" }, "message": { "text": "Uma rodada — toda fatia ganha esta semana.", "color": "#E3D9F2FF" }, "reward": { "title": { "text": "Você ganhou um benefício de garagem!", "color": "#FFFFFFFF" }, "code": "APEXPERK", "button": { "text": { "text": "Resgatar", "color": "#FFFFFFFF" }, "background": "#5B2B8FFF", "border": { "color": "#5B2B8FFF", "radius": 12 }, "action": { "type": "close" } } }, "segments": [ { "message": { "text": "5% de desconto", "color": "#FFFFFFFF" }, "color": "#5856D6FF", "weight": 1 }, { "message": { "text": "20% de desconto", "color": "#FFFFFFFF" }, "color": "#30B0C7FF", "weight": 1, "reward": { "title": { "text": "20% de desconto no seu próximo serviço", "color": "#FFFFFFFF" }, "code": "SPIN20", "button": { "text": { "text": "Resgatar oferta de serviço", "color": "#FFFFFFFF" }, "background": "#30B0C7FF", "border": { "color": "#30B0C7FF", "radius": 12 }, "action": { "type": "url", "url": "pushwoosh://service" } } } }, { "message": { "text": "Lavagem grátis", "color": "#FFFFFFFF" }, "color": "#FF2D55FF", "weight": 1 } ] }}stories
Anchor link toSlides de tela cheia com barras de progresso na parte superior, semelhantes às histórias de redes sociais.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
showClose | boolean | sim | Mostrar um botão de fechar (✕) |
loop | boolean | sim | Reiniciar do primeiro slide após o último |
items | Item[] | sim | Slides (pelo menos 1) |
Item de histórias:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
title | Text | não | Título |
message | Text | não | Subtítulo |
image | string | não | Imagem de fundo do slide |
buttons | Button[] | sim | Botões de CTA na parte inferior (pode estar vazio) |
duration | number | sim | Duração do slide, em 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": "O especial do Inferno Verde", "color": "#FFFFFFFF" }, "buttons": [ { "text": { "text": "Configure o seu", "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": "A lenda peso-pena, renascida", "color": "#FFFFFFFF" }, "buttons": [], "duration": 4 } ] }}video
Anchor link toVídeo em tela cheia com texto e botões por cima.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
showClose | boolean | sim | Mostrar um botão de fechar (✕) |
loop | boolean | sim | Repetir a reprodução |
muted | boolean | sim | Iniciar sem som |
url | string | sim | URL do vídeo (HLS ou MP4) |
poster | string | não | Pôster mostrado antes do início da reprodução |
fallback | string | não | Imagem mostrada se o vídeo falhar ao reproduzir |
title | Text | não | Título |
message | Text | não | Texto do corpo |
buttons | Button[] | sim | Botões de CTA na parte inferior (pode estar vazio) |
{ "displayType": "video", "video": { "showClose": true, "loop": true, "muted": true, "url": "https://example.com/reveal.mp4", "poster": "https://example.com/poster.jpg", "title": { "text": "A revelação", "color": "#FFFFFFFF" }, "message": { "text": "Assista-o em movimento antes de qualquer um.", "color": "#EBEBEBFF" }, "buttons": [ { "text": { "text": "Compre a linha", "color": "#FFFFFFFF" }, "background": "#0E72E5FF", "border": { "color": "#0E72E5FF", "radius": 14 }, "action": { "type": "url", "url": "pushwoosh://sale" } } ] }}Localização
Anchor link toIn-apps nativos reutilizam o mesmo mecanismo de localização exato que os Rich Media HTML: valores de string em native-config.json podem carregar placeholders {{key|type|default}}, e as traduções residem em um arquivo pushwoosh.json ao lado dele, no mesmo formato descrito em Adicionando pushwoosh.json. Um placeholder pode aparecer em qualquer campo de string, em qualquer profundidade (um título, um rótulo de botão, uma URL de imagem, uma URL de ação).
Conteúdo dinâmico
Anchor link toCampos de texto — title, message, text do botão, reward.title/reward.message e message do item/segmento — também aceitam Conteúdo dinâmico e Sintaxe Liquid: o mesmo atalho {Tag|modifier|default} e as tags Liquid {% %}/{{ }} usadas no conteúdo de push e e-mail. O Pushwoosh resolve isso por destinatário antes que a mensagem seja enviada, da mesma forma que faz para push e e-mail.
Ao construir um modelo no editor de in-app nativo do Painel de Controle, os tokens inseridos são renderizados como chips na visualização ao vivo, e Salvar é bloqueado se o Liquid de um campo de texto não for analisado corretamente.
Integração do SDK
Anchor link toDepois de adicionar o módulo SDK de in-app nativo ao seu aplicativo, as mensagens são exibidas automaticamente. Nenhum código extra é necessário para mostrar mensagens acionadas por um push, Customer Journey, postEvent ou pela caixa de entrada.
O SDK também expõe uma pequena API para controle manual:
- iOS:
Pushwoosh.inApp(móduloPushwooshInApp) - Android:
PushwooshInAppUi(módulopushwoosh-inapp-ui)
| Capacidade | iOS | Android |
|---|---|---|
| Mostrar uma configuração diretamente (teste/uso manual) | Pushwoosh.inApp.present(config) | PushwooshInAppUi.present(configJson) |
| Observar ciclo de vida e cliques | delegate (PWInAppMessageDelegate) | delegate (InAppMessageDelegate) |
| Verificar se algo está na tela | isPresenting | isPresenting |
| Dispensar o que estiver sendo exibido no momento | dismiss() | dismiss() |
| Pausar / retomar exibição | isPaused | isPaused |
Aplicar limitação de maxDisplays / cooldown | setFrequencyCapEnabled(_:) | setFrequencyCapEnabled(...) |
Callbacks do delegate (todos disparados na thread principal): shouldDisplay (retorne false para suprimir uma mensagem antes que ela seja exibida, por exemplo, em uma tela de checkout), willPresent, didPresent, didClose e clickedAction (disparado quando o usuário toca em uma ação de url, antes que a URL seja aberta).
O iOS adicionalmente relata rewardRevealed e rewardClaimed para os modelos gamificados scratchcard e spinwheel.
// iOSPushwoosh.inApp.delegate = selfPushwoosh.inApp.setFrequencyCapEnabled(true)// AndroidPushwooshInAppUi.delegate = thisPushwooshInAppUi.setFrequencyCapEnabled(true)