Przekierowywanie powiadomień typu push przez deep linki w React Native
Ustaw linki uniwersalne i linki aplikacji, połącz Pusher Beams z aplikacją React Native oraz powiąż adresy URL w sieci z ekranami natywnymi, aby kliknięcie powiadomienia otwierało właściwe miejsce.
Powiadomienie typu push jest przydatne tylko wtedy, gdy po jego kliknięciu użytkownik trafia na ekran, o którym mówi. Wyobraź sobie aplikację React Native przeznaczoną dla studentów zagranicznych wybierających uczelnie: e-maile pozostają niewyświetlone, a SMS-y są niepewne w różnych krajach, dlatego powiadomienia typu push muszą działać poprawnie, gdy ktoś z zespołu rekrutacyjnego pisze do nich. Tutaj skonfigurujesz łączenia głębokie na obu platformach, dołączysz adres strony internetowej do każdego powiadomienia Pusher Beams oraz przetłumaczysz ten adres na ścieżkę w React Navigation, testując przy tym każdy etap.
Dlaczego łączenia głębokie zawierają adres docelowy powiadomienia
Gdy tworzono tę konfigurację, pakiety Pusher używane w tym przypadku nie były w stanie przekazać spersonalizowanego ciężaru danych powiadomienia do części JavaScript aplikacji React Native na Androidzie. Najprostszym rozwiązaniem, podobnym do prośby o zmianę w Android SDK, jest wykorzystanie głębokiego łączenia: powiadomienie na Androidzie zawiera zwykły link do strony internetowej, kliknięcie w niego jest traktowane jak kliknięcie w ten link, a istniejąca w aplikacji funkcja obsługi głębokich łączeń decyduje, jakie ekran ma zostać otwarte.
Sytuacja ta mogła się od tamtej pory zmienić, dlatego najpierw sprawdź aktualne wersje SDK Beams. Warstwa routingu jest przydatna we wszystkich przypadkach, ponieważ obsługuje również linki z e-maili, SMS-ów lub czatów.
Krótka podsumowanie głębokiego łączenia
Deep linking umożliwia aplikacji przypisywanie sobie linków do własnej strony internetowej i ich otwieranie w sposób natywny. Link taki jak https://test.com/message/abc powinien otwierać aplikację bezpośrednio na wiadomości abc, zamiast uruchamiać przeglądarkę. iOS nazywa to uniwersalnymi linkami, a Android – linkami aplikacyjnymi; obie platformy wymagają jednak, aby twój domenowy serwer opublikował plik potwierdzający, że ufa aplikacji.
Ustawianie deep linking na obu platformach
Opublikuj pliki weryfikacyjne w katalogu .well-known
Stwórz katalog /.well-known/ w korzeniu swojej strony internetowej. Dla Androida dodaj plik /.well-known/assetlinks.json, który informuje, że twoja aplikacja może obsługiwać wszystkie linki w tym domenie. Zastąp miejsca zastępcze nazwą pakietu twojej aplikacji oraz wartością SHA-256 certyfikatu, który ją podpisuje:
[{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "package_name",
"sha256_cert_fingerprints":
["package_cert_fingerprint"]
}
}]
Jeśli Google Play ponownie podpisuje Twoje aplikacje, użyj odcisku palca klucza podpisywania Play, a nie klucza używanego do przesyłania plików – w przeciwnym razie weryfikacja zawiedzie tylko w środowisku produkcyjnym.
Dla iOS dodaj plik o nazwie /.well-known/apple-app-site-association (bez rozszerzenia). Zawiera on identyfikator Twojej aplikacji, który łączy ID zespołu i ID paczki, oraz ścieżki URL, które aplikacja powinna przechwytywać:
{
"applinks": {
"apps": [],
"details": [
{
"appID": "appId",
"paths": [ "/paths-you-want-to-support", "/messenger"]
}
]
}
}
Serwuj oba pliki przez HTTPS z dokładnie tym samym domenem, bez przekierowań. To klasyczna struktura applinks; Apple dodało później nowszy format, więc sprawdź aktualną dokumentację.
Włącz powiązane domeny w iOS
iOS wymaga również uprawnień, które wskazują mu, do których domen należy aplikacja. W Xcode otwórz Capabilities, włącz Associated Domains i dodaj każdą domenę, którą chcesz połączyć, zazwyczaj w formie applinks:twojadomena.com.
Ogłaszanie filtrów intencji w Androidzie
W systemie Android głębokie linki są deklarowane za pomocą filtrów intencji w pliku AndroidManifest.xml. Aby obsłużyć linki typu /messenger/*, główna aktywność musi mieć filtr intencji dla działania VIEW z kategoriami DEFAULT i BROWSABLE, a także element data określający schemat https, hosta oraz prefiks ścieżki /messenger. Dodanie parametru android:autoVerify="true" poleca systemowi sprawdzenie pliku assetlinks.json, dzięki czemu aplikacja może otwierać te linki bez pokazywania okna wyboru.
Słuchanie połączeń w komponencie korzeniowym
Komponent główny aplikacji powinien wykonywać dwie czynności: odczytywać URL, który uruchomił aplikację przy pierwszym starcie, oraz przysłuchiwać się do URL-ów przychodzących w trakcie jej działania. Moduł Linking w React Native zapewnia obie te funkcje za pomocą metody getInitialURL() oraz słuchacza zdarzeń url. Oba powinny wywoływać ten sam obsługujący funkcję, który na razie może jedynie zapisywać URL do logu:
const handleDeepLink = (url: string) => console.log(url);
Przetestuj linki przed dodawaniem powiadomień
Zanim dodasz funkcję powiadomień, sprawdź najpierw działanie głębokiego łączenia. Prostym sposobem jest wysłanie linku do siebie za pośrednictwem aplikacji do komunikacji, takiej jak Slack, na każdym urządzeniu testowym i jego otwarcie. Jeśli konfiguracja jest poprawna, link otworzy twoją aplikację zamiast przeglądarki, a zapisany URL będzie odpowiadał temu, który otworzyłeś. Izolacja tej warstwy oznacza, że późniejsze błędy w routowaniu powiadomień będą dotyczyć wyłącznie problemów z przesyłaniem danych.
Dodawanie Pusher Beams do aplikacji
Wymagania wstępne i most społecznościowy
Potrzebujesz konta Pusher Beams oraz musisz ukończyć kroki konfiguracji dla usługi push Apple oraz Google Firebase.
Należy pamiętać, że w momencie pisania tej integracji Pusher nie oferował oficjalnego pakietu dla React Native. Mostem użytym tutaj jest pakiet społecznościowy react-native-pusher-push-notifications, który łączy natywne SDK Beams z JavaScript i nie jest wspierany przez Pusher. Wymagał on niewielkich modyfikacji, aby działać w tym ustawieniu. Na Androidzie kluczową zmianą była odmiana SDK push-notifications-android firmy Pusher, która przekształca kliknięcie powiadomienia w otwarcie łącza głębokiego. Nieoficjalny most w połączeniu z odmianą SDK stanowi obowiązek konserwacyjny: należy utrzymywać określone wersje i ponownie rozważyć tę opcję, gdy zmienią się oficjalne SDK.
Zainstaluj SDK Swift na iOS
Część dla iOS polega na użyciu Swift SDK od Pusher, które instaluje się tutaj za pomocą Carthage. Dodaj następujący wiersz do pliku /ios/Cartfile, a następnie uruchom polecenie carthage bootstrap, aby je pobrać i zbudować:
github "pusher/push-notifications-swift" ~> 1.3.0
Ta wersja była aktualna dla tej integracji; nowsze projekty prawdopodobnie będą używać nowszego SDK oraz innego menedżera zależności.
Następnie postępuj zgodnie z instrukcjami ręcznej instalacji pakietu mostowego dla obu platform, ponieważ elementy natively zaimplementowane są dostosowywane indywidualnie.
Wysyłanie testowej powiadomienia
Powiadomienia na iOS docierają tylko do urządzeń fizycznych, a nie do symulatorów, więc miej pod ręką prawdziwy iPhone.
Aby wysłać testowy komunikat push, użyj konsoli Debug w panelu sterowania Pusher lub klienta HTTP takiego jak Postman, który wywołuje API Beams publish. Wystarczy ciało żądania zawierające sekcję iOS i sekcję Android; w tym ustawieniu część dla iOS ustala liczbę odznak na 5, a część dla Android zawiera adres URL strony internetowej, która powinna zostać otwarta.
Gdy wszystko jest poprawnie skonfigurowane, iOS sam przetwarza treść powiadomienia, natomiast Android otwiera aplikację za pomocą intencji View z adresem URL twojej strony. Na obu platformach twój obsługiwacz powinien zapisywać coś w rodzaju https://yourdomain/messenger/abcde. Na iOS ikona aplikacji powinna dodatkowo pokazywać odznakę z numerem 5.
Zmiana URL na trasy React Navigation
Ostatnim krokiem jest zastąpienie tymczasowego mechanizmu logowania rzeczywistym routowaniem. Powszechnie stosowanym rozwiązaniem jest użycie React Router na stronie internetowej oraz React Navigation w aplikacji React Native. Mała biblioteka pomocnicza, która mapuje ścieżki internetowe na ścieżki natywne, umożliwia wspólne wykorzystanie logiki i zapobiega temu, by przemianowana ścieżka internetowa potajemnie zakłócała nawigację w aplikacji.
Dzielenie się stałymi ścieżek pomiędzy wersją internetową a natywną
Oba zbiory kodu definiują swoje ścieżki jako stałe. W wersji internetowej narzędzie do budowania ścieżek zwraca albo konkretną ścieżkę, albo wzorzec, z którym React Router dokonuje dopasowania; w wersji natywnej odpowiednikiem jest nazwa ekranu, przy czym identyfikator rozmowy jest przekazywany oddzielnie jako parametr nawigacji:
// 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
Gdy obie strony są wyrażone jako stałe, można w jednej tabeli określić, która ścieżka internetowa odpowiada której ekranowi natywnemu:
const ROUTE_MATCHES: IRouteMatches = [
{
webPath: ROUTE.MESSENGER_CONVERSATION(),
rnPath: APP_STACK_ROUTES.MESSENGER_CONVERSATION_SCREEN
}
];
Wywołanie ROUTE.MESSENGER_CONVERSATION() bez żadnego argumentu zwraca wzorzec /messenger/:conversationId, więc tabela przechowuje ten sam wzorzec, który używa router strony internetowej. Przeniesienie nazwy trasy webowej automatycznie aktualizuje mapowanie. Aby ponownie zapoznać się z funkcjonowaniem tych wzorców webowych, sprawdź podstawy React Router.
Odbijanie nadchodzących URL-ów
Obsługa czasami uruchamia się więcej niż raz na jeden kliknięcie, na przykład gdy zarówno sprawdzenie początkowego URL-u, jak i słuchacz zgłaszają istnienie łącza, co powoduje pojawienie się duplikatowych ekranów. Przesyłanie każdego URL-u przez Subject w RxJS oraz zastosowanie funkcji debounceTime(100) sprowadza serię wywołań do jednego, które następnie trafia do 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);
Kompromis: dwa różne linki w ciągu 100 ms łączą się w jeden ostatni, co jest akceptowalne przy klikaniu w powiadomienia.
Rozbicie URL na części
processUrl najpierw dzieli URL na protokół, hosta, ścieżkę i ciąg zapytania za pomocą wyrażenia regularnego (narzędzie takie jak RegExr pomaga przy jego dostosowywaniu). Dekonstrukcja pomija pełne dopasowanie oraz grupę zapytania, która nadal zawiera ?:
const REGEX_DECONSTRUCT_URL = /^(.*?):\/\/(.*?)(\/.*?)(\?(.*))?$/;const deconstructedUrl = REGEX_DECONSTRUCT_URL.exec(url);
if (!deconstructedUrl) return;
const [originalUrl, protocol, tld, path, ignore, querystring] = deconstructedUrl;
Należy pamiętać, że to wzorzec wymaga ścieżki: prosty https://yourdomain bez końcowej kreski nie zostanie dopasowany, a funkcja zwróci wynik wcześniej. Jest to w porządku dla linków powiadomień, ale warto o tym wiedzieć, jeśli używasz tego narzędzia gdzie indziej.
Dopasowanie ścieżki do tabeli tras
Gdy elementy zostały wydobyte, mechanizm obsługi przechodzi dalej tylko do linków HTTPS w Twoim domenie, a następnie przegląda ROUTE_MATCHES aż do momentu, gdy jakaś pozycja zaakceptuje daną ścieżkę:
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;
}
}
}
Sprawdzenie tld.includes("twojadomena") jest wygodne, ale niewystarczające – zaakceptowałoby ono również hosta taki jak twojadomena.attacker.example. System operacyjny weryfikuje linki uniwersalne i aplikacyjne, więc ryzyko jest ograniczone, ale dokładna lista dopuszczalnych hostów to tanie rozwiązanie poprawiające bezpieczeństwo, szczególnie gdy mechanizm obsługi jest współdzielony z innymi źródłami URL.
Wewnątrz matchPath wzorzec webPath jest sprawdzany w odniesieniu do dostarczonej ścieżki za pomocą path-to-regexp – tej samej biblioteki, od której korzysta React Router. Gdy następuje dopasowanie, wydobyte parametry, takie jak conversationId, stają się parametrami odpowiadającego ekranu rnPath, a aplikacja przechodzi tam. Ponieważ ta operacja odbywa się poza jakimkolwiek komponentem, uruchamia ją niewielka usługa nawigacyjna przechowująca referencję do głównego navigatora, jak opisano w dokumentacji React Navigation dla nawigacji bez użycia właściwości navigation.
Podsumowanie
Gdy te elementy są już w miejscu, aplikacja obsługuje dwa zadania poprzez jedną ścieżkę kodu:
- Linki do Twojej strony internetowej, niezależnie od tego, czy pochodzą z e-maila, SMS-a czy czatu, otwierają odpowiedni ekran w aplikacji natywnej.
To, co ułatwia utrzymanie tego rozwiązania, to traktowanie adresu URL strony jako jedynego opisu celu, przy czym tabela służy do konwertowania adresów URL na lokalne ścieżki. Testuj każdą warstwę osobno, wzmocnij kontrolę hosta i obserwuj oficjalne SDK Beams: jeśli przekazują one dane do JavaScript, możesz zrezygnować z własnej wersji i zachować warstwę routingu.
Powiązane materiały
- Planowanie aktualizacji Expo SDK 58: iOS 27, React Native 0.88 i nowe narzędzia — Praktyczny przegląd wersji beta Expo SDK 58: jakie zmiany dotyczą iOS 27 i React Native 0.88, które funkcje są eksperymentalne oraz jak bezpiecznie przetestować aktualizację.
- Tworzenie konfigurowalnego komponentu Select dla React Native Paper — Przewodnik po projektowaniu i udostępnieniu kodu react-native-paper-select w formie otwartego oprogramowania, obejmujący funkcje wyszukiwania, wielokrotnego wyboru elementów, list podzielonych na sekcje oraz kwestie związane z wydajnością.
- Łączenie modułu Turbo wewnątrz aplikacji od początku do końca za pomocą React Native Codegen — Definiowanie typizowanej specyfikacji, uruchamianie procesu generowania kodu oraz implementacja modułu Turbo na iOS i Android przy użyciu metod synchronicznych, Promise, callback oraz event-emitter.
- Notyfikacje w React Native: uprawnienia, kanały oraz life-cycle FCM — Dowiedz się, jak uprawnienia, kanały Androida, tokeny FCM oraz mechanizmy obsługi stanów pierwszego planu, tła i wyjścia współpracują ze sobą w systemie notyfikacji React Native z Notifee.