Syntaxe des modèles in-app natifs
Les in-apps natifs sont rendus directement par le SDK. Aucune WebView n’est impliquée. Au lieu d’une page index.html, la ressource ZIP contient un fichier native-config.json qui décrit le message sous forme de données structurées (type de mise en page, textes, couleurs, images, boutons). Le SDK lit ce fichier et dessine la vue native correspondante, ce qui offre des animations plus fluides et de meilleures performances qu’une page web intégrée.
Ce guide documente le schéma native-config.json : champs, types et exemples pour chaque type d’affichage. Pour le format classique basé sur HTML, consultez la Syntaxe des modèles Rich Media.
Prérequis
Anchor link toLes in-apps natifs nécessitent :
- iOS : SDK 7.2.0 ou version ultérieure (7.2.1+ pour bannière, carrousel et feuille)
- Android : SDK 6.10.0 ou version ultérieure (6.10.1+ pour bannière, carrousel et feuille)
Tous les types d’affichage ne sont pas encore disponibles sur les deux plateformes. Vérifiez la Compatibilité des plateformes avant de vous fier à un format spécifique.
Compatibilité des plateformes
Anchor link to| Type d’affichage | 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 | ✅ | Pas encore disponible |
| scratchcard | ✅ | Pas encore disponible |
| spinwheel | ✅ | Pas encore disponible |
Structure du modèle
Anchor link toUn modèle in-app natif est une archive ZIP, identique à un modèle Rich Media classique, à l’exception que la racine contient un fichier native-config.json au lieu de index.html :
<template>.zip├── native-config.json ← requis, mise en page et contenu├── pushwoosh.json ← optionnel, localisation (voir ci-dessous)Les images et vidéos référencées depuis native-config.json (image, poster, fallback, url sur pip/video) doivent être des URL HTTPS absolues. Le SDK les charge via le réseau. Il ne lit pas les fichiers locaux de l’archive.
La configuration elle-même est un objet JSON unique :
{ "displayType": "<type>", "<type>": { /* bloc de contenu pour ce type */ } }displayType sélectionne l’un des dix formats ci-dessous. L’objet sous la clé correspondante contient le contenu de ce format. Une configuration avec un displayType inconnu, un bloc de contenu manquant ou une liste requise vide (items pour carousel/stories, segments pour spinwheel) est invalide. Le SDK ignore son affichage plutôt que de rendre une mise en page défectueuse.
Les paramètres de livraison (dates de début/fin et limitation de la fréquence) ne font pas partie de native-config.json. Ils sont configurés de la même manière que pour tout autre in-app, dans l’étape des Paramètres d’affichage de la campagne.
La limitation de la fréquence nécessite en outre une activation explicite côté SDK pour prendre effet sur les in-apps natifs. Voir Intégration SDK.
Couleurs
Anchor link toChaque valeur de couleur est une chaîne hexadécimale CSS : #RGB, #RGBA, #RRGGBB ou #RRGGBBAA. Le # initial est requis dans les quatre formes.
Blocs de construction partagés
Anchor link toCes objets plus petits sont réutilisés dans plusieurs types d’affichage.
Texte
Anchor link to| Champ | Type | Requis | Description |
|---|---|---|---|
text | string | oui | Le contenu textuel |
color | string | oui | Couleur du texte |
{ "text": "Spin for a garage perk", "color": "#FFFFFFFF" }Bordure
Anchor link to| Champ | Type | Requis | Description |
|---|---|---|---|
color | string | oui | Couleur de la bordure |
radius | number | oui | Rayon des coins, en points |
{ "color": "#0E72E5FF", "radius": 12 }Couverture
Anchor link toUne image optionnelle sur une couleur de fond. Utilisé par fullscreen et scratchcard.
| Champ | Type | Requis | Description |
|---|---|---|---|
image | string | non | URL de l’image de couverture |
background | string | oui | Couleur de fond affichée sous (ou à la place de) l’image |
{ "image": "https://example.com/cover.jpg", "background": "#1A1A1EFF" }Action
Anchor link toUne union discriminée sur type :
| Variante | Champs | Description |
|---|---|---|
{ "type": "close" } | Aucun | Ferme l’in-app |
{ "type": "url", "url": string } | url requis | Ouvre une URL ou un deep link |
{ "type": "url", "url": "pushwoosh://sale" }Bouton
Anchor link to| Champ | Type | Requis | Description |
|---|---|---|---|
text | Text | oui | Étiquette du bouton |
background | string | oui | Couleur de remplissage du bouton |
border | Border | oui | Bordure du bouton |
action | Action | oui | Action déclenchée lors de l’appui |
spinButton (spinwheel) et revealButton (scratchcard) utilisent la même forme sans action. Leur comportement (faire tourner la roue, révéler la carte) est intégré.
{ "text": { "text": "Book a test drive", "color": "#FFFFFFFF" }, "background": "#0E72E5FF", "border": { "color": "#0E72E5FF", "radius": 12 }, "action": { "type": "url", "url": "pushwoosh://sale" }}Récompense
Anchor link toLe panneau de récompense affiché par scratchcard et spinwheel. Une récompense valide a un title ou un code.
| Champ | Type | Requis | Description |
|---|---|---|---|
title | Text | non | Titre de la récompense |
message | Text | non | Description de la récompense |
code | string | non | Code promo, affiché avec un bouton de copie |
button | Button | non | Bouton de confirmation avec sa propre action |
{ "title": { "text": "20% off detailing", "color": "#111111FF" }, "message": { "text": "Valid for any full-detail booking this month.", "color": "#555555FF" }, "code": "APEX20", "button": { "text": { "text": "Book detailing", "color": "#FFFFFFFF" }, "background": "#B3227CFF", "border": { "color": "#B3227CFF", "radius": 12 }, "action": { "type": "url", "url": "pushwoosh://detailing" } }}Types d’affichage
Anchor link tobanner
Anchor link toUne barre compacte ancrée en haut ou en bas de l’écran.
| Champ | Type | Requis | Description |
|---|---|---|---|
showClose | boolean | oui | Afficher un bouton de fermeture (✕) |
position | top | bottom | oui | Bord de l’écran |
background | string | oui | Couleur de fond de la barre |
image | string | non | Vignette à gauche |
title | Text | non | Titre sur une seule ligne, tronqué par des points de suspension |
message | Text | non | Corps du texte, jusqu’à 2 lignes |
action | Action | oui | Déclenchée lorsque la barre elle-même est touchée |
autoDismiss | number | non | Fermeture automatique après ce nombre de secondes. Omettre pour la conserver jusqu’à sa fermeture |
{ "displayType": "banner", "banner": { "showClose": true, "position": "bottom", "background": "#4B5057FF", "image": "https://example.com/thumb.jpg", "title": { "text": "Alpine A110 just dropped", "color": "#FFFFFFFF" }, "message": { "text": "The featherweight icon — tap to see the build", "color": "#FFFFFFFF" }, "action": { "type": "url", "url": "pushwoosh://product/x6f" }, "autoDismiss": 6 }}carousel
Anchor link toUn ensemble de cartes plein écran, balayables, avec des points indicateurs de page.
| Champ | Type | Requis | Description |
|---|---|---|---|
showClose | boolean | oui | Afficher un bouton de fermeture (✕) |
items | Item[] | oui | Cartes (au moins 1) |
Élément du carrousel :
| Champ | Type | Requis | Description |
|---|---|---|---|
title | Text | non | Titre de la carte |
message | Text | non | Sous-titre de la carte |
image | string | non | Image de la carte |
action | Action | non | Déclenchée lorsque la carte est touchée |
{ "displayType": "carousel", "carousel": { "showClose": true, "items": [ { "image": "https://example.com/card-1.jpg", "title": { "text": "AMG GT R", "color": "#FFFFFFFF" }, "message": { "text": "585 hp biturbo V8 — just landed", "color": "#FFFFFFFF" }, "action": { "type": "url", "url": "pushwoosh://product/n6fx" } }, { "image": "https://example.com/card-2.jpg", "title": { "text": "Alpine A110", "color": "#FFFFFFFF" }, "message": { "text": "Featherweight icon — limited allocation", "color": "#FFFFFFFF" }, "action": { "type": "url", "url": "pushwoosh://product/x6f" } } ] }}fullscreen
Anchor link toUne image de couverture bord à bord avec du texte et des boutons par-dessus.
| Champ | Type | Requis | Description |
|---|---|---|---|
showClose | boolean | oui | Afficher un bouton de fermeture (✕) |
cover | Cover | oui | Image et couleur de fond |
title | Text | non | Titre |
message | Text | non | Corps du texte |
buttons | Button[] | oui | Boutons en bas (peut être vide) |
{ "displayType": "fullscreen", "fullscreen": { "showClose": true, "cover": { "image": "https://example.com/hero.jpg", "background": "#1A1A1EFF" }, "title": { "text": "Pure Maranello", "color": "#FFFFFFFF" }, "message": { "text": "The prancing horse, reimagined.", "color": "#EBEBEBFF" }, "buttons": [ { "text": { "text": "Reserve now", "color": "#FFFFFFFF" }, "background": "#0E72E5FF", "border": { "color": "#0E72E5FF", "radius": 8 }, "action": { "type": "url", "url": "pushwoosh://sale" } }, { "text": { "text": "Not now", "color": "#FFFFFFFF" }, "background": "#00000000", "border": { "color": "#FFFFFF99", "radius": 8 }, "action": { "type": "close" } } ] }}modal
Anchor link toUne carte centrée.
| Champ | Type | Requis | Description |
|---|---|---|---|
showClose | boolean | oui | Afficher un bouton de fermeture (✕) |
dimBackground | boolean | oui | Assombrir l’écran derrière la carte |
background | string | oui | Couleur de fond de la carte |
image | string | non | Image de couverture |
title | Text | non | Titre |
message | Text | non | Corps du texte |
buttons | Button[] | oui | Boutons sous le texte (peut être vide) |
{ "displayType": "modal", "modal": { "showClose": true, "dimBackground": true, "background": "#FFFFFFFF", "image": "https://example.com/cover.jpg", "title": { "text": "The GT R has landed", "color": "#4B5057FF" }, "message": { "text": "585 hp — now in the showroom.", "color": "#4B5057FF" }, "buttons": [ { "text": { "text": "Book a test drive", "color": "#FFFFFFFF" }, "background": "#0E72E5FF", "border": { "color": "#0E72E5FF", "radius": 12 }, "action": { "type": "url", "url": "pushwoosh://sale" } }, { "text": { "text": "Not now", "color": "#4B5057FF" }, "background": "#FFFFFFFF", "border": { "color": "#4B5057FF", "radius": 12 }, "action": { "type": "close" } } ] }}Une fenêtre vidéo flottante en picture-in-picture ancrée dans un coin de l’écran.
| Champ | Type | Requis | Description |
|---|---|---|---|
showClose | boolean | oui | Afficher un bouton de fermeture (✕) |
position | bottom-right | bottom-left | top-right | top-left | oui | Coin de l’écran |
loop | boolean | oui | Lecture en boucle |
muted | boolean | oui | Démarrer en sourdine |
url | string | oui | URL de la vidéo |
poster | string | non | Affiche montrée avant le début de la lecture |
fallback | string | non | Image montrée si la lecture de la vidéo échoue |
width | number | oui | Largeur de la fenêtre en pourcentage de la largeur de l’écran, limitée entre 15 et 70 |
aspectRatio | number | oui | Rapport hauteur/largeur de la fenêtre |
borderRadius | number | non | Rayon des coins de la fenêtre, en points |
action | Action | non | Déclenchée lorsque la fenêtre elle-même est touchée |
Il n’y a pas de boutons configurables sur pip. Les contrôles de la fenêtre (agrandir en plein écran, sourdine, fermer) sont fournis par le système.
{ "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 toUne carte avec la récompense cachée sous une couche de film à gratter.
| Champ | Type | Requis | Description |
|---|---|---|---|
showClose | boolean | oui | Afficher un bouton de fermeture (✕) |
background | string | string[] | oui | Couleur de fond de la carte, ou arrêts de dégradé |
revealThreshold | number | oui | Fraction du film qui doit être grattée (0–1) avant que la récompense ne se révèle |
cover | Cover | oui | La couche de film. Sans image, une indication « grattez ici » est affichée sur la couleur de fond |
revealButton | Button (no action) | non | Bouton « Révéler instantanément » |
title | Text | non | Titre |
message | Text | non | Corps du texte |
reward | Reward | oui | Le prix caché sous le film |
{ "displayType": "scratchcard", "scratchcard": { "showClose": true, "background": ["#3A1C71FF", "#B3227CFF", "#E0503AFF"], "revealThreshold": 0.55, "cover": { "background": "#C9CDD6FF" }, "revealButton": { "text": { "text": "Reveal without scratching", "color": "#3A1C71FF" }, "background": "#F2DFF5FF", "border": { "color": "#F2DFF5FF", "radius": 10 } }, "title": { "text": "Your loyalty reward", "color": "#FFFFFFFF" }, "message": { "text": "Scratch the foil to reveal this week's garage perk.", "color": "#F2DFF5FF" }, "reward": { "title": { "text": "20% off detailing", "color": "#111111FF" }, "message": { "text": "Valid for any full-detail booking this month.", "color": "#555555FF" }, "code": "APEX20", "button": { "text": { "text": "Book detailing", "color": "#FFFFFFFF" }, "background": "#B3227CFF", "border": { "color": "#B3227CFF", "radius": 12 }, "action": { "type": "url", "url": "pushwoosh://detailing" } } } }}sheet
Anchor link toUne carte épinglée au bord inférieur, avec une poignée de glissement.
| Champ | Type | Requis | Description |
|---|---|---|---|
showClose | boolean | oui | Afficher un bouton de fermeture (✕) |
dimBackground | boolean | oui | Assombrir l’écran derrière la feuille |
background | string | oui | Couleur de fond de la feuille |
image | string | non | Image de couverture |
title | Text | non | Titre |
message | Text | non | Corps du texte |
buttons | Button[] | oui | Boutons sous le texte (peut être vide) |
{ "displayType": "sheet", "sheet": { "showClose": true, "dimBackground": true, "background": "#FFFFFFFF", "image": "https://example.com/cover.jpg", "title": { "text": "Your quote is ready", "color": "#000000FF" }, "message": { "text": "Guaranteed buyout for your A110: $68,500.", "color": "#000000FF" }, "buttons": [ { "text": { "text": "Get guaranteed quote", "color": "#FFFFFFFF" }, "background": "#0E72E5FF", "border": { "color": "#0E72E5FF", "radius": 12 }, "action": { "type": "url", "url": "pushwoosh://sale" } } ] }}spinwheel
Anchor link toUne roue de la fortune avec des segments pondérés et un bouton central.
| Champ | Type | Requis | Description |
|---|---|---|---|
showClose | boolean | oui | Afficher un bouton de fermeture (✕) |
background | string | string[] | oui | Couleur de fond de la carte, ou arrêts de dégradé |
winIndex | number | oui | Index (base 0) du segment gagnant |
spinButton | Button (no action) | oui | Bouton central |
title | Text | non | Titre |
message | Text | non | Corps du texte |
reward | Reward | oui | Récompense pour le tour gagnant (solution de repli pour les segments sans la leur) |
loseTitle | Text | non | Titre affiché en cas de perte |
segments | Segment[] | oui | Segments de la roue (le SDK attend 2–12) |
Segment :
| Champ | Type | Requis | Description |
|---|---|---|---|
message | Text | oui | Étiquette du segment |
color | string | non | Couleur du segment. Omettre pour une palette de secours appliquée autour de la roue |
weight | number | oui | Taille relative du segment |
reward | Reward | non | Récompense spécifique au segment |
{ "displayType": "spinwheel", "spinwheel": { "showClose": true, "background": ["#1B1B46FF", "#5B2B8FFF", "#B0338AFF"], "winIndex": 1, "spinButton": { "text": { "text": "SPIN", "color": "#1B1B46FF" }, "background": "#F2C94CFF", "border": { "color": "#D9A02BFF", "radius": 36 } }, "title": { "text": "Spin for a garage perk", "color": "#FFFFFFFF" }, "message": { "text": "One spin — every slice wins this week.", "color": "#E3D9F2FF" }, "reward": { "title": { "text": "You won a garage perk!", "color": "#FFFFFFFF" }, "code": "APEXPERK", "button": { "text": { "text": "Claim", "color": "#FFFFFFFF" }, "background": "#5B2B8FFF", "border": { "color": "#5B2B8FFF", "radius": 12 }, "action": { "type": "close" } } }, "segments": [ { "message": { "text": "5% off", "color": "#FFFFFFFF" }, "color": "#5856D6FF", "weight": 1 }, { "message": { "text": "20% off", "color": "#FFFFFFFF" }, "color": "#30B0C7FF", "weight": 1, "reward": { "title": { "text": "20% off your next service", "color": "#FFFFFFFF" }, "code": "SPIN20", "button": { "text": { "text": "Claim service deal", "color": "#FFFFFFFF" }, "background": "#30B0C7FF", "border": { "color": "#30B0C7FF", "radius": 12 }, "action": { "type": "url", "url": "pushwoosh://service" } } } }, { "message": { "text": "Free wash", "color": "#FFFFFFFF" }, "color": "#FF2D55FF", "weight": 1 } ] }}stories
Anchor link toDiapositives en plein écran avec des barres de progression en haut, similaires aux stories des réseaux sociaux.
| Champ | Type | Requis | Description |
|---|---|---|---|
showClose | boolean | oui | Afficher un bouton de fermeture (✕) |
loop | boolean | oui | Redémarrer à partir de la première diapositive après la dernière |
items | Item[] | oui | Diapositives (au moins 1) |
Élément des stories :
| Champ | Type | Requis | Description |
|---|---|---|---|
title | Text | non | Titre |
message | Text | non | Sous-titre |
image | string | non | Image de fond de la diapositive |
buttons | Button[] | oui | Boutons CTA en bas (peut être vide) |
duration | number | oui | Durée de la diapositive, en secondes |
{ "displayType": "stories", "stories": { "showClose": true, "loop": false, "items": [ { "image": "https://example.com/slide-1.jpg", "title": { "text": "AMG GT R", "color": "#FFFFFFFF" }, "message": { "text": "The Green Hell special", "color": "#FFFFFFFF" }, "buttons": [ { "text": { "text": "Configure yours", "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": "The featherweight legend, reborn", "color": "#FFFFFFFF" }, "buttons": [], "duration": 4 } ] }}video
Anchor link toVidéo en plein écran avec du texte et des boutons par-dessus.
| Champ | Type | Requis | Description |
|---|---|---|---|
showClose | boolean | oui | Afficher un bouton de fermeture (✕) |
loop | boolean | oui | Lecture en boucle |
muted | boolean | oui | Démarrer en sourdine |
url | string | oui | URL de la vidéo (HLS ou MP4) |
poster | string | non | Affiche montrée avant le début de la lecture |
fallback | string | non | Image montrée si la lecture de la vidéo échoue |
title | Text | non | Titre |
message | Text | non | Corps du texte |
buttons | Button[] | oui | Boutons CTA en bas (peut être vide) |
{ "displayType": "video", "video": { "showClose": true, "loop": true, "muted": true, "url": "https://example.com/reveal.mp4", "poster": "https://example.com/poster.jpg", "title": { "text": "The reveal", "color": "#FFFFFFFF" }, "message": { "text": "Watch it move before anyone else.", "color": "#EBEBEBFF" }, "buttons": [ { "text": { "text": "Shop the lineup", "color": "#FFFFFFFF" }, "background": "#0E72E5FF", "border": { "color": "#0E72E5FF", "radius": 14 }, "action": { "type": "url", "url": "pushwoosh://sale" } } ] }}Localisation
Anchor link toLes in-apps natifs réutilisent exactement le même mécanisme de localisation que les Rich Media HTML : les valeurs de chaîne dans native-config.json peuvent contenir des placeholders {{key|type|default}}, et les traductions se trouvent dans un fichier pushwoosh.json à côté, dans le même format décrit dans Ajout de pushwoosh.json. Un placeholder peut apparaître dans n’importe quel champ de chaîne, à n’importe quelle profondeur (un titre, une étiquette de bouton, une URL d’image, une URL d’action).
Contenu dynamique
Anchor link toLes champs de texte — title, message, text du bouton, reward.title/reward.message, et message de l’élément/segment — acceptent également le Contenu dynamique et la syntaxe Liquid : le même raccourci {Tag|modifier|default} et les balises Liquid {% %}/{{ }} utilisés dans le contenu des notifications push et des e-mails. Pushwoosh les résout pour chaque destinataire avant l’envoi du message, de la même manière que pour les notifications push et les e-mails.
Lors de la création d’un modèle dans l’éditeur d’in-app natif du Control Panel, les jetons insérés s’affichent sous forme de puces dans l’aperçu en direct, et la Sauvegarde est bloquée si le Liquid d’un champ de texte ne peut pas être analysé.
Intégration SDK
Anchor link toUne fois que vous avez ajouté le module SDK in-app natif à votre application, les messages s’affichent automatiquement. Aucun code supplémentaire n’est requis pour afficher les messages déclenchés par une notification push, un Customer Journey, postEvent ou la boîte de réception.
Le SDK expose également une petite API pour un contrôle manuel :
- iOS :
Pushwoosh.inApp(modulePushwooshInApp) - Android :
PushwooshInAppUi(modulepushwoosh-inapp-ui)
| Capacité | iOS | Android |
|---|---|---|
| Afficher une configuration directement (test/utilisation manuelle) | Pushwoosh.inApp.present(config) | PushwooshInAppUi.present(configJson) |
| Observer le cycle de vie et les clics | delegate (PWInAppMessageDelegate) | delegate (InAppMessageDelegate) |
| Vérifier si quelque chose est à l’écran | isPresenting | isPresenting |
| Fermer ce qui est actuellement affiché | dismiss() | dismiss() |
| Mettre en pause / reprendre l’affichage | isPaused | isPaused |
Appliquer la limitation maxDisplays / cooldown | setFrequencyCapEnabled(_:) | setFrequencyCapEnabled(...) |
Callbacks du délégué (tous déclenchés sur le thread principal) : shouldDisplay (retournez false pour supprimer un message avant qu’il ne s’affiche, par exemple sur un écran de paiement), willPresent, didPresent, didClose, et clickedAction (déclenché lorsque l’utilisateur appuie sur une action url, avant l’ouverture de l’URL).
iOS signale en plus rewardRevealed et rewardClaimed pour les modèles ludiques scratchcard et spinwheel.
// iOSPushwoosh.inApp.delegate = selfPushwoosh.inApp.setFrequencyCapEnabled(true)// AndroidPushwooshInAppUi.delegate = thisPushwooshInAppUi.setFrequencyCapEnabled(true)