Syntaxe des modèles d'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 de Rich Media.
Prérequis
Anchor link toLes in-apps natifs nécessitent :
- iOS : SDK 7.2.0 ou ultérieur (7.2.1+ pour bannière, carrousel et feuille)
- Android : SDK 6.10.0 ou ultérieur (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 le Support des plateformes avant de vous fier à un format spécifique.
Support 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 | ✅ | Pas encore disponible |
| pip | ✅ | Pas encore disponible |
| scratchcard | ✅ | Pas encore disponible |
| spinwheel | ✅ | Pas encore disponible |
Structure du modèle
Anchor link toUn modèle d’in-app natif est une archive ZIP, identique à un modèle de 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 plafonnement 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, à l’étape Paramètres d’affichage de la campagne.
Le plafonnement 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 du 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": "Tournez pour un avantage garage", "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 lien profond (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 à 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": "Réserver un essai", "color": "#FFFFFFFF" }, "background": "#0E72E5FF", "border": { "color": "#0E72E5FF", "radius": 12 }, "action": { "type": "url", "url": "pushwoosh://sale" }}Récompense
Anchor link toLe panneau de prix 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, rendu avec un bouton de copie |
button | Button | non | Bouton de confirmation avec sa propre action |
{ "title": { "text": "20 % de réduction sur le detailing", "color": "#111111FF" }, "message": { "text": "Valable pour toute réservation de detailing complet ce mois-ci.", "color": "#555555FF" }, "code": "APEX20", "button": { "text": { "text": "Réserver le 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 au bord supérieur ou inférieur 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é avec 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": "L'Alpine A110 vient de sortir", "color": "#FFFFFFFF" }, "message": { "text": "L'icône poids plume — touchez pour voir la configuration", "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 de 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": "V8 biturbo de 585 ch — vient d'arriver", "color": "#FFFFFFFF" }, "action": { "type": "url", "url": "pushwoosh://product/n6fx" } }, { "image": "https://example.com/card-2.jpg", "title": { "text": "Alpine A110", "color": "#FFFFFFFF" }, "message": { "text": "L'icône poids plume — allocation limitée", "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": "Le cheval cabré, réimaginé.", "color": "#EBEBEBFF" }, "buttons": [ { "text": { "text": "Réserver maintenant", "color": "#FFFFFFFF" }, "background": "#0E72E5FF", "border": { "color": "#0E72E5FF", "radius": 8 }, "action": { "type": "url", "url": "pushwoosh://sale" } }, { "text": { "text": "Pas maintenant", "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": "La GT R est arrivée", "color": "#4B5057FF" }, "message": { "text": "585 ch — maintenant dans le showroom.", "color": "#4B5057FF" }, "buttons": [ { "text": { "text": "Réserver un essai", "color": "#FFFFFFFF" }, "background": "#0E72E5FF", "border": { "color": "#0E72E5FF", "radius": 12 }, "action": { "type": "url", "url": "pushwoosh://sale" } }, { "text": { "text": "Pas maintenant", "color": "#4B5057FF" }, "background": "#FFFFFFFF", "border": { "color": "#4B5057FF", "radius": 12 }, "action": { "type": "close" } } ] }}Une fenêtre vidéo flottante en Picture-in-Picture (incrustation d’image) ancrée à 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 affiché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, mettre en 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": "Révéler sans gratter", "color": "#3A1C71FF" }, "background": "#F2DFF5FF", "border": { "color": "#F2DFF5FF", "radius": 10 } }, "title": { "text": "Votre récompense de fidélité", "color": "#FFFFFFFF" }, "message": { "text": "Grattez le film pour révéler l'avantage garage de cette semaine.", "color": "#F2DFF5FF" }, "reward": { "title": { "text": "20 % de réduction sur le detailing", "color": "#111111FF" }, "message": { "text": "Valable pour toute réservation de detailing complet ce mois-ci.", "color": "#555555FF" }, "code": "APEX20", "button": { "text": { "text": "Réserver le 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": "Votre devis est prêt", "color": "#000000FF" }, "message": { "text": "Rachat garanti pour votre A110 : 68 500 $.", "color": "#000000FF" }, "buttons": [ { "text": { "text": "Obtenir un devis garanti", "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 leur propre récompense) |
loseTitle | Text | non | Titre affiché en cas de perte |
segments | Segment[] | oui | Segments de la roue (le SDK attend entre 2 et 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": "TOURNEZ", "color": "#1B1B46FF" }, "background": "#F2C94CFF", "border": { "color": "#D9A02BFF", "radius": 36 } }, "title": { "text": "Tournez pour un avantage garage", "color": "#FFFFFFFF" }, "message": { "text": "Un tour — chaque part est gagnante cette semaine.", "color": "#E3D9F2FF" }, "reward": { "title": { "text": "Vous avez gagné un avantage garage !", "color": "#FFFFFFFF" }, "code": "APEXPERK", "button": { "text": { "text": "Réclamer", "color": "#FFFFFFFF" }, "background": "#5B2B8FFF", "border": { "color": "#5B2B8FFF", "radius": 12 }, "action": { "type": "close" } } }, "segments": [ { "message": { "text": "5 % de réduction", "color": "#FFFFFFFF" }, "color": "#5856D6FF", "weight": 1 }, { "message": { "text": "20 % de réduction", "color": "#FFFFFFFF" }, "color": "#30B0C7FF", "weight": 1, "reward": { "title": { "text": "20 % de réduction sur votre prochain service", "color": "#FFFFFFFF" }, "code": "SPIN20", "button": { "text": { "text": "Réclamer l'offre de service", "color": "#FFFFFFFF" }, "background": "#30B0C7FF", "border": { "color": "#30B0C7FF", "radius": 12 }, "action": { "type": "url", "url": "pushwoosh://service" } } } }, { "message": { "text": "Lavage gratuit", "color": "#FFFFFFFF" }, "color": "#FF2D55FF", "weight": 1 } ] }}stories
Anchor link toDiapositives 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 de 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": "L'édition spéciale Green Hell", "color": "#FFFFFFFF" }, "buttons": [ { "text": { "text": "Configurez la vôtre", "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 légende poids plume, réincarnée", "color": "#FFFFFFFF" }, "buttons": [], "duration": 4 } ] }}video
Anchor link toVidéo 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 affiché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": "La révélation", "color": "#FFFFFFFF" }, "message": { "text": "Regardez-la bouger avant tout le monde.", "color": "#EBEBEBFF" }, "buttons": [ { "text": { "text": "Découvrir la gamme", "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 Ajouter 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).
Intégration du SDK
Anchor link toUne fois que vous avez ajouté le module SDK pour 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 un 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 le plafonnement 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 ludifiés scratchcard et spinwheel.
// iOSPushwoosh.inApp.delegate = selfPushwoosh.inApp.setFrequencyCapEnabled(true)// AndroidPushwooshInAppUi.delegate = thisPushwooshInAppUi.setFrequencyCapEnabled(true)