Acheminement des notifications push via des liens profonds dans React Native
Configurer des liens universels et des liens d’application, intégrer Pusher Beams dans une application React Native, et associer des URL web à des écrans natifs afin qu’un clic sur une notification ouvre le bon endroit.
Pourquoi les liens profonds indiquent la destination de la notification
Lors de la création de cette configuration, les paquets Pusher utilisés ici ne pouvaient pas transmettre la charge utile personnalisée d’une notification à la partie JavaScript d’une application React Native sur Android. La solution la plus simple, similaire à une demande de modification pour le SDK Android, consiste à utiliser le lien profond : la notification Android contient un lien vers un site web normal, le tap est traité comme un clic sur ce lien, et le mécanisme de gestion des liens profonds existant dans l’application décide quel écran ouvrir.
Cela a pu changer depuis, il convient donc de vérifier d’abord les versions actuelles des SDK Beams. La couche de routage ci-dessous reste utile dans tous les cas, car elle gère également les liens provenant d’e-mails, de SMS ou de chats.
Un bref résumé des liens profonds
Le lien profond permet à l’application d’accéder aux liens vers son propre site web et de les ouvrir nativement. Un lien tel que https://test.com/message/abc devrait ouvrir directement l’application sur le message abc au lieu de lancer un navigateur. iOS les appelle des liens universels, Android les appelle des liens d’application, et dans les deux cas, votre domaine doit publier un fichier prouvant qu’il fait confiance à l’application.
Mettre en place le lien profond sur les deux plateformes
Publier les fichiers de vérification dans le répertoire .well-known
Créez un répertoire /.well-known/ à la racine de votre site web. Pour Android, ajoutez /.well-known/assetlinks.json, qui indique que votre application peut gérer tous les URL du domaine. Remplacez les éléments de remplacement par le nom du package de votre application ainsi que l’empreinte SHA-256 du certificat qui le signe :
[{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "package_name",
"sha256_cert_fingerprints":
["package_cert_fingerprint"]
}
}]
Si Google Play resigne vos versions, utilisez l’empreinte digitale de la clé de signature de Play et non celle de votre clé d’envoi, sinon la vérification échouera uniquement en environnement de production.
Pour iOS, ajoutez un fichier nommé /.well-known/apple-app-site-association (sans extension). Il indique l’identifiant de votre application, qui combine l’ID d’équipe et l’ID de paquet, ainsi que les chemins URL que l’application doit intercepter :
{
"applinks": {
"apps": [],
"details": [
{
"appID": "appId",
"paths": [ "/paths-you-want-to-support", "/messenger"]
}
]
}
}
Fournissez ces deux fichiers via HTTPS depuis le domaine exact, sans redirections. Il s’agit du format classique applinks ; Apple a depuis ajouté un format plus récent, il convient donc de consulter sa documentation actuelle.
Activer les domaines associés sur iOS
iOS a également besoin d’une autorisation qui lui indique quels domaines appartiennent à l’application. Dans Xcode, ouvrez Capabilities, activez Associated Domains, puis ajoutez chaque domaine que vous souhaitez connecter, généralement sous la forme applinks:yourdomain.com.
Déclarer des filtres d’intention sur Android
Sur Android, les liens profonds sont déclarés à l’aide de filtres d’intention dans AndroidManifest.xml. Pour capturer les liens /messenger/*, l’activité principale doit disposer d’un filtre d’intention pour l’action VIEW avec les catégories DEFAULT et BROWSABLE, ainsi que d’un élément data spécifiant le schéma https, votre hôte et un préfixe de chemin /messenger. L’ajout de android:autoVerify="true" indique au système de vérifier assetlinks.json afin que l’application puisse ouvrir ces liens sans afficher de boîte de dialogue de sélection.
Écouter les liens dans le composant racine
Le composant racine de l’application doit effectuer deux tâches : lire l’URL qui a lancé l’application lors d’un démarrage depuis zéro, et écouter les URLs qui arrivent pendant qu’elle est déjà en cours d’exécution. Le module Linking de React Native offre ces deux fonctionnalités grâce à la méthode getInitialURL() ainsi qu’à un écouteur d’événements url. Ces deux mécanismes doivent appeler le même gestionnaire, qui pour l’instant peut simplement enregistrer l’URL :
const handleDeepLink = (url: string) => console.log(url);
Tester les liens avant d’ajouter des notifications
Vérifier le fonctionnement du deep linking indépendamment avant d’y ajouter des notifications par push. Une méthode simple consiste à envoyer un lien vers soi-même via une application de messagerie comme Slack sur chaque appareil de test et à l’ouvrir. Si la configuration est correcte, le lien ouvrira votre application plutôt que le navigateur, et l’URL enregistrée correspondra à celle que vous avez cliquée. En isolant cette étape, toute notification mal dirigée ne pourra être qu’un problème lié aux notifications par push.
Ajout des Pusher Beams à l’application
Prérequis et le pont communautaire
Vous avez besoin d’un compte Pusher Beams, et vous devez suivre les étapes de configuration pour le service de notification d’Apple ainsi que pour Google Firebase.
Prévoyez que Pusher ne proposait pas de package officiel pour React Native au moment où cette intégration a été écrite. Le pont utilisé ici est le package communautaire react-native-pusher-push-notifications, qui relie les SDK natifs Beams au JavaScript et n’est pas pris en charge par Pusher. Il a nécessité de légères modifications pour fonctionner dans ce contexte. Sur Android, la modification principale a consisté à utiliser une version modifiée du SDK push-notifications-android de Pusher, qui transforme un clic sur une notification en ouverture d’un lien profond. L’utilisation d’un pont non officiel ainsi que d’un SDK modifié implique un engagement de maintenance : il faut fixer les versions et réévaluer ce choix en cas de changement des SDK officiels.
Installer le SDK Swift sur iOS
Le côté iOS dépend du SDK Swift de Pusher, installé ici à l’aide de Carthage. Ajoutez la ligne suivante dans /ios/Cartfile, puis exécutez carthage bootstrap pour le télécharger et le compiler :
github "pusher/push-notifications-swift" ~> 1.3.0
Cette version était à jour pour cette intégration ; les projets plus récents utiliseront probablement un SDK plus récent ainsi que, éventuellement, un autre gestionnaire de dépendances.
Ensuite, suivez les étapes d’installation manuelle du paquet de pont pour les deux plateformes, car les composants natifs sont personnalisés.
Envoi d’une notification de test
Les notifications sur iOS n’arrivent que sur des appareils physiques, pas sur des simulateurs, il faut donc disposer d’un iPhone réel.
Pour envoyer une notification de test, utilisez soit la console de débogage dans le tableau de bord de Pusher, soit un client HTTP tel que Postman pour appeler l’API de publication Beams. Un corps de requête contenant une section iOS et une section Android suffit ; dans cette configuration, la partie iOS définit un compteur de badges à 5, tandis que la partie Android contient l’URL du site web qui doit s’ouvrir.
Lorsque tout est correctement configuré, iOS traite lui-même le contenu de la notification, tandis qu’Android ouvre l’application via une intention View contenant l’URL de votre site web. Sur les deux plateformes, votre gestionnaire doit enregistrer quelque chose comme https://yourdomain/messenger/abcde. Sur iOS, l’icône de l’application doit en outre afficher un badge avec le nombre 5.
Transformer les URLs en routes React Navigation
La dernière étape consiste à remplacer le stub de journalisation par un routage réel. Une configuration courante utilise React Router sur le site web et React Navigation dans l’application React Native. Une petite utilité qui mappe les routes web aux routes natives permet à ces deux systèmes de partager leur logique et évite que la modification d’une URL web ne perturbe silencieusement la navigation de l’application.
Partager des constantes de route entre le web et le natif
Les deux bases de code définissent leurs routes en tant que constantes. Sur le web, un constructeur de route renvoie soit un chemin concret, soit le schéma contre lequel React Router effectue la correspondance ; sur le natif, l’équivalent est un nom d’écran, avec l’ID de la conversation transmis séparément en tant que paramètre de navigation :
// Web:
ROUTE = {
MESSENGER_CONVERSATION: (conversationId?: string) =>
conversationId
? `/messenger/${conversationId}`
: "/messenger/:conversationId"
}// Native:
APP_STACK_ROUTE: {
MESSENGER_CONVERSATION_SCREEN:
"app_stack_routes/messenger_conversation"
}
// native then has a params object with conversationId included
Comme les deux aspects sont exprimés sous forme de constantes, vous pouvez déclarer quel chemin web correspond à quel écran natif dans un seul tableau :
const ROUTE_MATCHES: IRouteMatches = [
{
webPath: ROUTE.MESSENGER_CONVERSATION(),
rnPath: APP_STACK_ROUTES.MESSENGER_CONVERSATION_SCREEN
}
];
L’appel de ROUTE.MESSENGER_CONVERSATION() sans argument renvoie le schéma /messenger/:conversationId, de sorte que la table stocke le même schéma que celui utilisé par le routeur du site web. Changer le nom d’une route web met à jour automatiquement la correspondance. Pour un rappel sur le fonctionnement de ces schémas web, consultez les bases de React Router.
Débouncing des URLs entrantes
Le gestionnaire est parfois déclenché plus d’une fois par tap, par exemple lorsque la vérification de l’URL initiale et l’écouteur signalent tous deux un lien, ce qui génère des écrans dupliqués. En faisant passer chaque URL à travers un Subject d’RxJS et en appliquant debounceTime(100), on regroupe les appels successifs en un seul, qui est ensuite envoyé à processUrl:
export const handleDeepLink = (url: string): void => {
if (!url) return;
onChangeUrl$.next(url);
};
const onChangeUrl$: Subject<string> = new Subject<string>();
const urlSubscription: Observable<string> = onChangeUrl$.pipe(debounceTime(100));
urlSubscription.subscribe(processUrl);
L’équilibre à trouver : deux liens différents apparaissant en moins de 100 ms se résument au dernier, ce qui est acceptable pour les clics sur des notifications.
Décomposer l’URL en parties
processUrl divise d’abord l’URL en protocole, hôte, chemin et chaîne de requête à l’aide d’une expression régulière (un outil comme RegExr est utile pour l’ajuster). La déstructuration ignore la correspondance complète ainsi que le groupe de requête contenant encore le ?:
const REGEX_DECONSTRUCT_URL = /^(.*?):\/\/(.*?)(\/.*?)(\?(.*))?$/;const deconstructedUrl = REGEX_DECONSTRUCT_URL.exec(url);
if (!deconstructedUrl) return;
const [originalUrl, protocol, tld, path, ignore, querystring] = deconstructedUrl;
Notez que ce modèle nécessite un chemin : une URL du type https://yourdomain sans barre oblique à la fin ne correspondra pas et la fonction renverra immédiatement une valeur. Cela convient bien pour les liens de notification, mais il est utile de le savoir si vous réutilisez cette fonction ailleurs.
Comparer le chemin avec la table des routes
Lorsque les éléments ont été extraits, le gestionnaire ne procède qu’avec les liens HTTPS sur votre domaine, puis parcourt ROUTE_MATCHES jusqu’à ce qu’une entrée accepte le chemin :
if (protocol === "https" && tld.includes("yourdomain")) {
for (let i = 0; i < ROUTE_MATCHES.length; i++) {
if (matchPath(ROUTE_MATCHES[i], path, querystring)) {
// loop until one matches
break;
}
}
}
La vérification tld.includes("yourdomain") est pratique mais peu stricte : elle accepterait également un hôte comme yourdomain.attacker.example. Le système d’exploitation vérifie les liens universels et ceux des applications, ce qui limite le risque, mais une liste blanche d’hôtes exacts constitue une amélioration simple lorsque d’autres sources URL partagent le même gestionnaire.
Dans matchPath, le motif webPath est comparé au chemin reçu à l’aide de path-to-regexp, la même bibliothèque que React Router utilise. Lorsqu’il y a correspondance, les paramètres extraits, tels que conversationId, deviennent les paramètres de l’écran rnPath correspondant, et l’application navigue vers celui-ci. Comme cette opération s’exécute en dehors de tout composant, un petit service de navigation qui contient la référence du navigateur racine le déclenche, comme l’indiquent les documents de React Navigation pour la navigation sans propriété navigation.
En résumé
Avec ces éléments en place, l’application gère deux tâches grâce à un seul chemin de code :
- Les liens vers votre site web, qu’ils proviennent d’e-mails, de SMS ou de chats, ouvrent l’écran correspondant dans l’application native.
Ce qui permet de maintenir ce système est de considérer l’URL du site web comme la seule description d’une destination, avec un tableau qui convertit les URLs en chemins natifs. Testez chaque couche séparément, renforcez la vérification de l’hôte, et consultez les SDK officiels Beams : s’ils transmettent des données au JavaScript, vous pouvez abandonner la version modifiée et conserver uniquement la couche de routage.
Lectures complémentaires
- Planifier une mise à jour de l’Expo SDK 58 : iOS 27, React Native 0.88 et nouvelles outils — Une présentation pratique de la version bêta de l’Expo SDK 58 : quels changements pour iOS 27 et React Native 0.88, quelles fonctionnalités sont expérimentales, et comment tester la mise à jour en toute sécurité.
- Création d’un composant Select configurable pour React Native Paper — Une présentation détaillée de la conception et du partage en open source de react-native-paper-select, abordant la recherche, les éléments de sélection multiples, les listes segmentées et les compromis en termes de performance.
- Connexion d’un module Turbo intégré de bout en bout avec React Native Codegen — Définition d’une spécification typée, exécution de codegen, et mise en œuvre d’un module Turbo sur iOS et Android à l’aide de méthodes synchrones, Promise, callback et émetteur d’événements.
- Notifications React Native : Permissions, canaux et cycle de vie de FCM — Découvrez comment les permissions, les canaux Android, les tokens FCM ainsi que les gestionnaires d’état en premier plan, en arrière-plan et à l’arrêt s’intègrent dans un système de notifications React Native avec Notifee.