Startseite / Artikel / Routen von Push-Benachrichtigungen über Deep Links in React Native

Routen von Push-Benachrichtigungen über Deep Links in React Native

Erstellen Sie universelle Links und App-Links, integrieren Sie Pusher Beams in eine React Native-App und verknüpfen Sie Web-URLs mit den entsprechenden nativen Bildschirmen, damit ein Klick auf eine Benachrichtigung den richtigen Ort öffnet.

1898 Wörter

Eine Push-Benachrichtigung ist nur nützlich, wenn das Tippen darauf den Benutzer auf den entsprechenden Bildschirm führt. Stellen Sie sich eine React Native-App für internationale Studenten vor, die Universitäten auswählen sollen: E-Mails bleiben ungelesen und SMS sind in anderen Ländern unzuverlässig, daher muss die Push-Benachrichtigung funktionieren, wenn ein Rekrutant schreibt. Hier konfigurieren Sie das Deep Linking auf beiden Plattformen, fügen einer jeder Pusher Beams-Benachrichtigung eine Website-URL hinzu und wandeln diese URL in einen React Navigation-Pfad um, wobei Sie dabei jede Ebene testen.

Warum Deep Links das Ziel der Benachrichtigung enthalten

Zu der Zeit, als diese Konfiguration erstellt wurde, konnten die hier verwendeten Pusher-Pakete den benutzerdefinierten Payload einer Benachrichtigung nicht an die JavaScript-Seite einer React Native-App auf Android weiterleiten. Die einfachste Lösung, ähnlich wie ein Pull Request für das Android SDK, besteht darin, Deep Linking zu nutzen: Die Android-Benachrichtigung enthält einen normalen Webseiten-Link, der Tap wird wie ein Klick auf diesen Link behandelt, und die vorhandene Deep-Link-Verarbeitung der App entscheidet, welches Bildschirm geöffnet wird.

Dies könnte seitdem sich geändert haben, daher sollten Sie zunächst die aktuellen Beams SDKs überprüfen. Die Routing-Schicht ist in jedem Fall nützlich, da sie auch Links aus E-Mails, SMS oder Chats bereitstellt.

Kurze Zusammenfassung zu Deep Linking

Deep Linking ermöglicht es der App, Verlinkungen zu Ihrer eigenen Website zu nutzen und sie direkt im eigenen Interface zu öffnen. Eine solche Verlinkung wie https://test.com/message/abc sollte die App direkt auf der Nachricht abc öffnen, anstatt einen Browser zu starten. iOS bezeichnet diese als Universal Links, Android als App Links – in beiden Fällen muss Ihre Domain eine Datei veröffentlichen, die beweist, dass sie der App vertraut.

Einrichtung von Deep Linking auf beiden Plattformen

Veröffentlichen Sie die Überprüfungsdateien unter .well-known

Erstellen Sie ein Verzeichnis /.well-known/ im Wurzelverzeichnis Ihrer Website. Für Android fügen Sie /.well-known/assetlinks.json hinzu, in dem angegeben wird, dass Ihre App alle URLs auf der Domain verarbeiten darf. Ersetzen Sie die Platzhalter durch den Paketnamen Ihrer Anwendung sowie den SHA-256-Fingerabdruck des Zertifikats, mit dem sie signiert wird:

[{
    "relation": ["delegate_permission/common.handle_all_urls"],
    "target": {
        "namespace": "android_app",
        "package_name": "package_name",
        "sha256_cert_fingerprints":
        ["package_cert_fingerprint"]
    }
}]

Falls Google Play Ihre Veröffentlichungen erneut signiert, verwenden Sie den Fingerabdruck der Play-Signierungsdatei und nicht den Ihrer Upload-Datei – andernfalls fehlt die Verifizierung nur in der Produktion.

Für iOS fügen Sie eine Datei namens /.well-known/apple-app-site-association (ohne Erweiterung) hinzu. Sie enthält Ihren App-Identifikator, der aus Team-ID und Bundle-ID besteht, sowie die URL-Pfade, die die App abfangen soll:

{
    "applinks": {
        "apps": [],
        "details": [
            {
                "appID": "appId",
                "paths": [ "/paths-you-want-to-support", "/messenger"]
            }
        ]
    }
}

Liefern Sie beide Dateien über HTTPS vom exakten Domainnamen aus, ohne Umleitungen. Dies ist die klassische applinks-Struktur; Apple hat seitdem ein neueres Format hinzugefügt, daher sollten Sie die aktuelle Dokumentation prüfen.

Assoziierte Domains auf iOS aktivieren

iOS benötigt außerdem eine Berechtigung, die angibt, zu welchen Domains die App Zugriff hat. Öffnen Sie in Xcode Capabilities, aktivieren Sie Associated Domains und fügen Sie jeden Domain hinzufügt, zu der Sie eine Verbindung herstellen möchten – in der Regel als applinks:yourdomain.com.

Intent-Filter auf Android deklarieren

Auf Android werden Deep Links mit Intent-Filtern in AndroidManifest.xml deklariert. Um /messenger/*-Links abzufangen, benötigt die Hauptaktivität einen Intent-Filter für die Aktion VIEW mit den Kategorien DEFAULT und BROWSABLE, sowie ein data-Element, das das https-Schema, Ihren Host sowie ein Pfadpräfix von /messenger angibt. Durch Hinzufügen von android:autoVerify="true" wird dem System mitgeteilt, assetlinks.json zu überprüfen, damit die App diese Links ohne Anzeige eines Auswahldialogs öffnen kann.

Links im Wurzelkomponenten abfangen

Die Wurzelebene der App sollte zwei Dinge erledigen: die URL lesen, die die App bei einem Neustart gestartet hat, sowie auf eingehende URLs lauschen, während die App bereits läuft. Das Linking-Modul von React Native bietet beides über die Methode getInitialURL() sowie einen Event-Listener für url. Beide sollten auf denselben Handler aufrufen, der vorerst lediglich die URL protokollieren kann:

const handleDeepLink = (url: string) => console.log(url);

Testen Sie die Links vor dem Hinzufügen von Benachrichtigungen

Überprüfen Sie zunächst das Deep Linking für sich allein, bevor Sie Push-Benachrichtigungen darauf aufbauen. Eine einfache Methode besteht darin, über eine Messaging-App wie Slack auf jedem Testgerät einen Link an sich selbst zu senden und diesen anzuklicken. Wenn die Konfiguration korrekt ist, öffnet sich Ihre App anstelle des Browsers und die protokollierte URL entspricht der, auf die geklickt wurde. Durch die Trennung dieser Komponenten kann ein späteres Fehlverhalten der Benachrichtigungen nur auf ein Problem mit den Push-Benachrichtigungen zurückzuführen sein.

Hinzufügen von Pusher Beams zur App

Voraussetzungen und die Community-Brücke

Sie benötigen ein Pusher Beams-Konto und müssen die Einrichtungsschritte für Apples Push-Dienst sowie Google Firebase abschließen.

Bedenken Sie, dass Pusher zum Zeitpunkt der Erstellung dieser Integration kein offizielles React Native-Paket anbot. Die hier verwendete Brücke ist das Community-Paket react-native-pusher-push-notifications, das die nativen Beams-SDKs mit JavaScript verbindet und von Pusher nicht unterstützt wird. Es mussten geringfügige Anpassungen vorgenommen werden, damit es in dieser Konfiguration funktioniert. Auf Android bestand die wichtigste Änderung in einem Fork des Pusher-push-notifications-android-SDKs, der ein Tippen auf eine Benachrichtigung in einen Deep-Link-Aufruf umwandelt. Eine inoffizielle Brücke zusammen mit einem geforkten SDK erfordert laufende Wartungsarbeiten: Versionen müssen festgelegt werden, und die Wahl muss überprüft werden, falls sich offizielle SDKs ändern.

Installieren des Swift-SDKs auf iOS

Die iOS-Seite setzt auf Pushers Swift SDK, das hier mit Carthage installiert wird. Fügen Sie die folgende Zeile zu /ios/Cartfile hinzu und führen Sie anschließend carthage bootstrap aus, um es herunterzuladen und zu kompilieren:

github "pusher/push-notifications-swift" ~> 1.3.0

Jene Version war für diese Integration aktuell; neuere Projekte verwenden vermutlich ein neueres SDK sowie möglicherweise einen anderen Abhängigkeitsmanager.

Folgen Sie anschließend den manuellen Installationsanleitungen des Bridge-Pakets für beide Plattformen, da die nativen Komponenten angepasst sind.

Sendung einer Testbenachrichtigung

Benachrichtigungen auf iOS erreichen nur physische Geräte, nicht Simulatoren – halten Sie daher ein echtes iPhone zur Hand.

Zum Senden eines Test-Push-Nachrichtens können Sie entweder die Debug-Konsole im Pusher-Dashboard oder einen HTTP-Client wie Postman verwenden, um die Beams-Publish-API aufzurufen. Ein Anfragekörper mit einer iOS- und einer Android-Seite reicht aus; dabei legt der iOS-Bereich eine Anzahl von 5 Badges fest, während der Android-Bereich die URL der Website enthält, die geöffnet werden soll.

Sobald alles richtig konfiguriert ist, verarbeitet iOS den Benachrichtigungsinhalt selbst, während Android die App über einen View-Intent mit der URL Ihrer Website öffnet. Auf beiden Plattformen sollte Ihr Handler etwas wie https://yourdomain/messenger/abcde protokollieren. Auf iOS sollte außerdem das App-Icon ein Badge mit der Zahl 5 anzeigen.

URLs in React Navigation-Routen umwandeln

Der letzte Schritt besteht darin, den Logging-Stub durch echtes Routing zu ersetzen. Eine gängige Konfiguration verwendet React Router auf der Website und React Navigation in der React Native-App. Ein kleines Hilfsprogramm, das Web-Routen auf native Routen abbildet, ermöglicht es beiden, Logik zu teilen, und verhindert, dass eine umbenannte Web-URL heimlich die App-Navigation stört.

Routenkonstanten zwischen Web und Native teilen

Sowohl die Codebasen definieren ihre Routen als Konstanten. Im Web gibt ein Route-Builder entweder einen konkreten Pfad oder das Muster zurück, gegen das React Router abgleicht; im Native-Modus entspricht dies einem Bildschirmnamen, wobei die Konversation-ID separat als Navigationsparameter übergeben wird:

// 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

Da beide Seiten als Konstanten dargestellt werden, können Sie in einer einzigen Tabelle angeben, welcher Web-Pfad auf welchen nativen Bildschirm abgebildet wird:

const ROUTE_MATCHES: IRouteMatches = [
    {
        webPath: ROUTE.MESSENGER_CONVERSATION(),
        rnPath: APP_STACK_ROUTES.MESSENGER_CONVERSATION_SCREEN
    }
];

Die Aufrufung von ROUTE.MESSENGER_CONVERSATION() ohne Argument gibt das Muster /messenger/:conversationId zurück, sodass die Tabelle dasselbe Muster speichert wie der Router der Website. Das Umbenennen einer Webroute aktualisiert die Zuordnung automatisch. Für einen Überblick darüber, wie diese Webmuster funktionieren, siehe React Router Grundlagen.

Abschwächen eingehender URLs

Der Handler triggert manchmal mehrmals pro Berührung, zum Beispiel wenn sowohl die Überprüfung der Initial-URL als auch der Listener auf einen Link hinweisen, was zu doppelten Ansichten führt. Durch das Übergeben jeder URL an einen RxJS Subject und die Anwendung von debounceTime(100) werden mehrfache Aufrufe zu einem einzigen zusammengefasst, der anschließend an processUrl weitergeleitet wird:

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);

Der Kompromiss: Zwei verschiedene Links innerhalb von 100 ms werden zum letzten zusammengefasst, was für Klicks auf Benachrichtigungen akzeptabel ist.

Die URL in Teile aufteilen

processUrl teilt die URL zunächst mithilfe einer regulären Ausdrucksregel in Protokoll, Host, Pfad und Abfragesatz auf (ein Tool wie RegExr hilft bei der Anpassung). Die Dekonstruktion überspringt die vollständige Übereinstimmung sowie die Abfragegruppe, die noch das ? enthält:

const REGEX_DECONSTRUCT_URL = /^(.*?):\/\/(.*?)(\/.*?)(\?(.*))?$/;const deconstructedUrl = REGEX_DECONSTRUCT_URL.exec(url);
    if (!deconstructedUrl) return;
    const [originalUrl, protocol, tld, path, ignore, querystring] = deconstructedUrl;

Bemerken Sie, dass dieses Muster einen Pfad erfordert: Ein reiner https://yourdomain ohne Schlussstrich stimmt nicht überein und die Funktion gibt frühzeitig zurück. Das ist für Benachrichtigungslinks in Ordnung, aber zu beachten, falls Sie den Helper an anderer Stelle wiederverwenden.

Den Pfad mit der Route-Tabelle abgleichen

Sobald die Teile extrahiert wurden, setzt der Handler nur bei HTTPS-Verknüpfungen auf Ihrer Domain fort und durchläuft anschließend ROUTE_MATCHES, bis eine Eintragung den Pfad akzeptiert:

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;
        }
    }
}

Die Überprüfung mit tld.includes("yourdomain") ist zwar praktisch, aber ungenau: Sie würde auch einen Host wie yourdomain.attacker.example akzeptieren. Das Betriebssystem überprüft universelle und Anwendungs-Verknüpfungen, wodurch das Risiko begrenzt ist. Eine genaue Host-Allowliste stellt jedoch eine kostengünstige Verbesserung dar, insbesondere wenn mehrere URL-Quellen denselben Handler nutzen.

In matchPath wird das Muster von webPath mithilfe von path-to-regexp – derselben Bibliothek, auf die React Router zurückgreift – mit dem eingehenden Pfad verglichen. Wenn eine Übereinstimmung vorliegt, werden die extrahierten Parameter wie conversationId zu den Parametern für das entsprechende rnPath-Bildschirm, und die App navigiert dorthin. Da dies außerhalb jeglicher Komponente abläuft, wird es von einem kleinen Navigationsservice ausgelöst, der die Referenz zum Root-Navigator enthält, wie es in den Dokumentationen von React Navigation für die Navigation ohne das navigation-Attribut beschrieben wird.

Zusammenfassung

Mit diesen Komponenten kann die App zwei Aufgaben über denselben Codepfad bewältigen:

  • Links zu Ihrer Website, egal ob sie über E-Mail, SMS oder Chat kommen, öffnen den passenden Bildschirm in der nativen App.
  • Push-Benachrichtigungen nutzen dieselbe Verarbeitung von Deep Links, sodass ein Tap den Benutzer direkt zur entsprechenden Konversation führt.
  • Was die Wartbarkeit gewährleistet, ist die Behandlung der Website-URL als einzige Beschreibung eines Ziels, wobei eine Tabelle URLs in native Routen umwandelt. Testen Sie jede Schicht getrennt voneinander, verschärfen Sie die Überprüfung des Hosts und beobachten Sie die offiziellen Beams SDKs: Wenn sie Payloads an JavaScript weiterleiten, können Sie die Fork-Version weglassen und die Routing-Schicht beibehalten.

    Verwandte Artikel