Powiadomienia w React Native: uprawnienia, kanały i cykl życia FCM
Dowiedz się, jak uprawnienia, kanały Androida, tokeny FCM oraz mechanizmy obsługi stanów pierwszego planu, tła i zakończenia działania współpracują ze sobą w systemie powiadomień React Native z Notifee.
Źródła: React Native Notifications & Permissions: A Complete Guide for Android and iOS
Dla użytkownika powiadomienie to jedna linijka tekstu, na przykład „Twoje zamówienie zostało wysłane”. Dla aplikacji, która je wysyła, ta linijka zależy od uprawnień w czasie wykonywania aplikacji, kanałów powiadomień Androida, tokenów urządzenia, Firebase Cloud Messaging (FCM), procesorów w tle oraz różnic pomiędzy platformami Android i iOS. Błąd na jakimkolwiek z tych poziomów powoduje, że powiadomienia nigdy nie przychodzą lub otwierają niewłaściwy ekran. Ten przewodnik omawia każdy z tych poziomów w kontekście Notifee i React Native Firebase, abyś mógł stworzyć system powiadomień, który zachowuje się przewidywalnie we wszystkich stanach aplikacji.
Powiadomienia lokalne i powiadomienia typu push
Powiadomienia lokalne
Powiadomienie lokalne jest tworzone na urządzeniu przez samą aplikację, bez udziału serwera. Typowym przykładem jest przypomnienie o spotkaniu:
Reminder
Your meeting starts in 10 minutes.
Aplikacja może je wyświetlać lub planować w trybie offline. Notifee oferuje jedną API dla React Native, która integruje się z systemami powiadomień natywnych obu platform.
Powiadomienia typu push
Powiadomienie typu push rozpoczyna się na serwerze. Twój backend przekazuje wiadomość do FCM, FCM dostarcza ją na urządzenie, a aplikacja React Native reaguje na to:
Your Backend
│
▼
Firebase Cloud Messaging
│
▼
Android / iOS Device
│
▼
React Native Application
FCM rozróżnia wiadomości powiadomieniowe (które system może dla ciebie wyświetlić) od wiadomości danych (dowolne dane w formacie klucz-wartość przeznaczone dla twojego kodu), a także może kierować się do pojedynczego urządzenia lub grupy urządzeń za pomocą tematów.
Pozwolenia są częścią stanu aplikacji
Instalacja aplikacji nie oznacza już automatycznie, że będzie ona wyświetlać powiadomienia. Od Android 13 POST_NOTIFICATIONS jest uprawnieniem działającym w czasie wykonywania aplikacji, a aplikacje przeznaczone dla Android 13 lub nowszych muszą o nie prosić przed wyświetleniem jakichkolwiek treści. iOS zawsze wymagał wyraźnej zgody użytkownika. Traktuj uprawnienia jako stan aplikacji, który może ulegać zmianie, a nie jako krok konfiguracyjny wykonywany raz na zawsze.
Rozsądny proces polega na sprawdzeniu statusu, wyjaśnieniu korzyści, a następnie zapytaniu użytkownika; każda odpowiedź pozwala aplikacji kontynuować działanie:
Application starts
│
▼
Check notification permission
│
├── Granted ───────► Continue
│
└── Not granted
│
▼
Explain the benefit
│
▼
Request permission
│
┌─────┴─────┐
▼ ▼
Granted Denied
│ │
▼ ▼
Notifications Continue
Instalacja bibliotek
Notifee zajmuje się wyświetlaniem powiadomień:
npm install @notifee/react-native
React Native Firebase dostarcza podstawowy moduł aplikacji oraz moduł do przesyłania wiadomości typu push:
npm install @react-native-firebase/app
npm install @react-native-firebase/messaging
Na iOS należy następnie zainstalować natywne zależności CocoaPods.
cd ios
pod install
cd ..
Konfiguracja natywna różni się w zależności od wersji React Native i ustawień projektu, a przesyłanie powiadomień na iOS wymaga również danych logowania APNs w Firebase oraz funkcji przesyłania powiadomień w Xcode. Należy kierować się aktualną dokumentacją każdej z bibliotek.
Wyświetlanie pierwszej powiadomienia natywnego
Celem pierwszego przykładu jest powiadomienie o następującym tytule i treści:
New Message
You have received a new message.
Najpierw należy zaimportować Notifee wraz z enum określającym poziom ważności używanym dla kanałów w Androidzie:
import notifee, {
AndroidImportance,
} from '@notifee/react-native';
Poniższa funkcja wykonywa trzy czynności po kolei: prosi o zgodę, tworzy (lub ponownie używa) kanał w Androidzie o nazwie default, a następnie wyświetla powiadomienie na tym kanału. Parametr pressAction o identyfikatorze default instruuje system Android, aby otworzył aplikację po kliknięciu w powiadomienie; bez niego kliknięcie nie ma żadnego efektu.
export async function displayNotification() {
// Request permission
await notifee.requestPermission(); // Create Android notification channel
const channelId = await notifee.createChannel({
id: 'default',
name: 'Default Notifications',
importance: AndroidImportance.DEFAULT,
}); // Display notification
await notifee.displayNotification({
title: 'New Message',
body: 'You have received a new message.', android: {
channelId,
pressAction: {
id: 'default',
},
},
});
}
Powtarzane wywoływanie createChannel jest bezpieczne; istniejący kanał po prostu zwraca swój identyfikator. Kanały są obowiązkowe w Android 8.0 (API 26) i nowszych wersjach. Funkcję tę można uruchomić za pomocą przycisku:
<Button
title="Show Notification"
onPress={displayNotification}
/>
Prośba o uprawnienie w Android 13
Pełna nazwa tego uprawnienia w systemie Android brzmi:
android.permission.POST_NOTIFICATIONS
React Native udostępnia je jako stałą w obiekcie PermissionsAndroid:
PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS
Uprawnienie to musi być również zadeklarowane za pomocą tagu uses-permission w pliku AndroidManifest.xml; w przeciwnym razie prośba zostanie cicho odrzucona. Należy zacząć od odpowiednich importów:
import {
PermissionsAndroid,
Platform,
} from 'react-native';
Pomocnik natychmiast zwraca true na iOS oraz w wersjach Android poniżej 13 (poziom API 33), gdzie powiadomienia są dostępne domyślnie. W pozostałych przypadkach wyświetla się dialog systemowy, a następnie informuje o tym, czy wynikem było GRANTED:
export async function requestAndroidNotificationPermission() {
if (Platform.OS !== 'android') {
return true;
} if (Platform.Version < 33) {
return true;
} const result = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS,
); return result === PermissionsAndroid.RESULTS.GRANTED;
}
Wezwij to z ekranu:
const granted =
await requestAndroidNotificationPermission();
i rozgałęź się na podstawie wyniku:
if (granted) {
console.log('Notifications allowed');
} else {
console.log('Notifications denied');
}
Android może zwrócić trzy znaczące wyniki:
GRANTED
DENIED
NEVER_ASK_AGAIN
NEVER_ASK_AGAIN wymaga szczególnej uwagi. Po wielokrotnych odmowach system przestaje pokazywać dialog, a odpowiedź można zmienić tylko w ustawieniach systemu.
Zadawanie pytania we właściwym momencie
Rozpoczynanie prośby systemowej od razu na pierwszym ekranie to powszechny i kosztowny błąd. Nowy użytkownik, który od razu zobaczy
"Allow MyApp to send notifications?"
nie ma kontekstu i najprawdopodobniej odmówi. Najpierw wyjaśnij, na przykład na ekranie powitania:
Welcome to MyApp
za którym idzie karta przedstawiająca, do czego służą powiadomienia, wraz z przyciskiem:
Enable notifications to receive:
✓ New messages
✓ Order updates
✓ Important alerts[Enable Notifications]
Tylko to kliknięcie uruchamia dialog systemu, więc prośba odpowiada na wybór użytkownika zamiast przerywać mu działanie.
Uzyskiwanie uprawnień w iOS za pomocą Notifee
W systemie iOS użytkownik musi wyraźnie zatwierdzić dostawę powiadomień. Notifee raportuje wynik za pomocą AuthorizationStatus:
import notifee, {
AuthorizationStatus,
} from '@notifee/react-native';
Poniższy pomocnik traktuje dwa statusy jako sukces. AUTHORIZED oznacza pełne uprawnienia. PROVISIONAL to tryb cichy systemu iOS, w którym powiadomienia są dostarczane bezpośrednio do Centrum Powiadomień, nie przerywając pracy użytkownika, a ten może je później zachować lub wyłączyć. Wszystko inne jest traktowane jako odmowa:
export async function requestIOSNotificationPermission() {
const settings = await notifee.requestPermission(); if (
settings.authorizationStatus ===
AuthorizationStatus.AUTHORIZED
) {
console.log('Notifications authorized');
return true;
} if (
settings.authorizationStatus ===
AuthorizationStatus.PROVISIONAL
) {
console.log('Notifications provisionally authorized');
return true;
} console.log('Notifications denied'); return false;
}
Prośba skierowana do systemu iOS może zawierać szereg opcji:
Alerts
Sounds
Badges
CarPlay
Announcements
Provisional notifications
Prosz o to tylko, czego faktycznie potrzebujesz; na przykład tymczasowe uprawnienia całkowicie pomijają prośbę o zatwierdzenie.
Jedna funkcja uprawnień dla całej aplikacji
Zamiast rozproszać sprawdzania na różnych ekranach, użyj jednego modułu usługowego:
src/
├── services/
│ └── notificationService.ts
├── screens/
├── components/
└── App.tsx
Plik z definicją usługi:
// notificationService.ts
Łączy ścieżki obu platform. Na Androidzie stosuje sprawdzenie wersji oraz żądanie w czasie wykonywania aplikacji; na iOS używa Notifee i akceptuje zarówno status zatwierdzony, jak i tymczasowy:
import {
Platform,
PermissionsAndroid,
} from 'react-native';import notifee, {
AuthorizationStatus,
} from '@notifee/react-native';export async function requestNotificationPermission() {
if (Platform.OS === 'android') {
if (Platform.Version < 33) {
return true;
} const result = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS,
); return result === PermissionsAndroid.RESULTS.GRANTED;
} const settings = await notifee.requestPermission(); return (
settings.authorizationStatus ===
AuthorizationStatus.AUTHORIZED ||
settings.authorizationStatus ===
AuthorizationStatus.PROVISIONAL
);
}
Ekrany następnie wywołują jedną funkcję:
const enabled = await requestNotificationPermission();
i działają na podstawie otrzymanej odpowiedzi:
if (enabled) {
console.log('Ready for notifications');
}
Nowsze wersje Notifee mogą również żądać POST_NOTIFICATIONS na Android 13 za pomocą requestPermission(); sprawdź aktualną dokumentację Notifee, jeśli wolisz jedno żądanie dla obu platform.
Kanały powiadomień w Android
Android 8.0 wprowadził kanały, aby użytkownicy mogli niezależnie kontrolować kategorie powiadomień. Aplikacja może mieć kilka takich kanałów:
MyApp
│
├── Messages
├── Orders
├── Promotions
└── Security
Każdy kanał ma swoją własną wagę i dźwięk. Wiadomości zazwyczaj powinny mieć wysoką wagę, aby pojawiały się natychmiast:
const messageChannel = await notifee.createChannel({
id: 'messages',
name: 'Messages',
importance: AndroidImportance.HIGH,
});
Gdy aktualizuje się zamówienia, można użyć domyślnego poziomu:
const orderChannel = await notifee.createChannel({
id: 'orders',
name: 'Orders',
importance: AndroidImportance.DEFAULT,
});
Wiadomość zawiera nazwę kanału, do którego należy. Wywołanie jest pokazane w dwóch częściach: najpierw tytuł, a potem treść:
await notifee.displayNotification({
title: 'New Message',
body: 'You received a new message.',
Następnie opcje Androida odnoszące się do identyfikatora kanału zwróconego przez createChannel:
android: {
channelId: messageChannel,
},
});
Kanały są przeznaczone dla użytkowników, a nie tylko stanowią ustawienie programistyczne. Użytkownicy mogą wyłączyć dźwięk lub zablokować kanał w ustawieniach systemu, a po utworzeniu kanału aplikacja nie może już programowo zmieniać jego ważności; to decyzja użytkownika. Wybieraj starannie lub użyj później nowego identyfikatora kanału.
Struktura dobrze sprawdzająca się w produkcji dzieli kanały według ich przeznaczenia:
notifications
│
├── messages
├── orders
├── payments
├── security
└── marketing
Odrębny kanał marketingowy pozwala użytkownikom wyłączać promocje bez utraty alertów bezpieczeństwa.
Jak pasuje FCM
W przypadku metody push FCM znajduje się pomiędzy twoim backendem a każdą zainstalowaną aplikacją:
┌─────────────────┐
│ Your Backend │
└────────┬────────┘
│
▼
┌─────────────────┐
│ FCM │
└────────┬────────┘
│
┌───────┴───────┐
▼ ▼
┌──────────┐ ┌──────────┐
│ Android │ │ iOS │
└──────────┘ └──────────┘
Pobieranie tokena urządzenia
Każda instalacja aplikacji może otrzymać token rejestracji FCM, a twój backend potrzebuje tego tokena, aby skierować się do konkretnego urządzenia. Wgraj moduł komunikacji:
import messaging from '@react-native-firebase/messaging';
i poproś o token:
export async function getFCMToken() {
const token = await messaging().getToken(); console.log('FCM Token:', token); return token;
}
W rzeczywistej aplikacji token jest wysyłany na twój serwer zaraz po jego uzyskaniu:
const token = await messaging().getToken();
await fetch('https://api.example.com/device-token', {
method: 'POST', headers: {
'Content-Type': 'application/json',
}, body: JSON.stringify({
token,
}),
});
Następnie backend przechowuje token w związku z zalogowanym użytkownikiem i urządzeniem:
User
│
├── userId
├── deviceId
└── fcmToken
Utrzymywanie tokenów w aktualności
Tokeny zmieniają się, na przykład po ponownej instalacji lub usunięciu danych aplikacji. Nasłuchuj zdarzeń odświeżania:
import messaging from '@react-native-firebase/messaging';
Nasłuchiwacz przekazuje każdy nowy token, a funkcja zwraca funkcję odwołania do anulowania subskrypcji:
export function subscribeToTokenRefresh() {
return messaging().onTokenRefresh(async token => {
console.log('New FCM token:', token); await sendTokenToBackend(token);
});
}
Minimalny pomocnik do przesyłania plików, pokazany ponownie w dwóch częściach, wykonuje żądanie JSON POST:
async function sendTokenToBackend(token: string) {
await fetch('https://api.example.com/device-token', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
}, body: JSON.stringify({
token,
}),
});
}
Wskazówki samego Firebase polegają na obsłudze zmian tokenów i przesyłaniu nowej wartości do serwera aplikacji za każdym razem, gdy dojdzie do zmiany. Po stronie serwera należy również usunąć tokeny, które FCM uzna za nieważne, aby uniknąć gromadzenia się starych urządzeń.
Wiadomości w tle nie są wyświetlane automatycznie
Najbardziej mylącym przypadkiem jest przyjście wiadomości push, gdy aplikacja jest otwarta:
User
│
▼
React Native App
│
▼
FCM message
Śledź wiadomości w tle:
import messaging from '@react-native-firebase/messaging';
const unsubscribe = messaging().onMessage(
async remoteMessage => {
console.log('Foreground message:', remoteMessage);
},
);
Jednak odbiór wiadomości i wyświetlenie powiadomienia systemowego to oddzielne kroki. Gdy aplikacja znajduje się na pierwszym planie, FCM przekazuje wiadomość do twojego kodu i sam nie wyświetla nic. Aby coś pokazać, przekaż treść do Notifee:
import notifee from '@notifee/react-native';
Podany poniżej obsługa zapewnia, że kanał istnieje, a następnie wyświetla powiadomienie; w przypadku, gdy wiadomość nie zawiera bloku notification (na przykład wiadomość zawierająca tylko dane), używa się tekstu domyślnego:
messaging().onMessage(async remoteMessage => {
const channelId = await notifee.createChannel({
id: 'messages',
name: 'Messages',
}); await notifee.displayNotification({
title:
remoteMessage.notification?.title ??
'New Message', body:
remoteMessage.notification?.body ??
'You have a new message.', android: {
channelId,
pressAction: {
id: 'default',
},
},
});
});
Możesz kontrolować prezentację, w tym pomijać ją, gdy odpowiedni ekran jest już widoczny.
Wiadomości w tle i stan wyłączenia
Gdy aplikacja znajduje się w tle, zachowanie się zmienia. Na Androidzie wiadomości powiadomień FCM trafiają bezpośrednio do pasku systemowego, nie wywołując twojej funkcji powrotnej w tle, a wiadomości łączące powiadomienia z danymi podlegają własnym regułom działania w tle. Na iOS wiadomości w tle zawierające tylko dane są przekazywane według możliwości systemu i mogą być opóźnione lub ograniczone.
React Native Firebase dostarcza dedykowaną obsługę dla wiadomości w tle:
import messaging from '@react-native-firebase/messaging';
messaging().setBackgroundMessageHandler(
async remoteMessage => {
console.log(
'Background message:',
remoteMessage,
);
},
);
Częstym błędem jest rejestracja tego obsługującego zdarzenia wewnątrz komponentu React, jak pokazano w poniższym fragmencie:
function App() {
messaging().setBackgroundMessageHandler(...);
return <Navigation />;
}
Przebudzenie aplikacji w tle może nie załadować żadnego komponentu, więc obsługujące zdarzenie w ogóle nie istniałoby. Należy je zarejestrować w punkcie wejścia:
// index.js
import messaging from '@react-native-firebase/messaging';messaging().setBackgroundMessageHandler(
async remoteMessage => {
console.log(
'Background message:',
remoteMessage,
);
},
);
a dopiero potem zarejestrować komponent korzeniowy:
AppRegistry.registerComponent(
appName,
() => App,
);
Reakcja na kliknięcia powiadomień
Kliknięcie powinno zazwyczaj przenieść użytkownika w konkretne miejsce:
Notification
│
▼
User taps
│
▼
Application opens
│
▼
Order Details
Załóżmy, że wiadomość zawiera w swoich danych identyfikator zamówienia:
{
"orderId": "12345"
}
Dla aplikacji przywracanych z tła, funkcja onNotificationOpenedApp jest wywoływana, gdy użytkownik kliknie w powiadomienie. Funkcja zwrotna odczytuje identyfikator z remoteMessage.data i może stamtąd przekierować użytkownika:
messaging().onNotificationOpenedApp(
remoteMessage => {
const orderId =
remoteMessage.data?.orderId;
if (orderId) {
// Navigate to order details
console.log(
'Open order:',
orderId,
);
}
},
);
Gdy aplikacja została całkowicie zamknięta
Jeśli aplikacja została zakończona, sekwencja wygląda w ten sposób:
App completely closed
│
▼
User receives notification
│
▼
User taps notification
│
▼
App launches
W takim przypadku onNotificationOpenedApp nie zostanie uruchomiony. Zamiast tego należy poprosić o powiadomienie, które uruchomiło aplikację, raz podczas startu:
const remoteMessage =
await messaging().getInitialNotification();
if (remoteMessage) {
const orderId =
remoteMessage.data?.orderId; console.log(
'App opened from notification:',
orderId,
);
}
Oba metody obejmują jedynie powiadomienia wyświetlone przez FCM. Dotknięcia powiadomień, które sami wyświetliliśmy za pomocą Notifee, takich jak te w tle, trafiają przez własne API zdarzeń Notifee (onForegroundEvent, onBackgroundEvent oraz getInitialNotification). Obsługa tylko jednej rodziny powiadomień jest częstą przyczyną błędów typu „dotknięcie nie ma żadnego efektu”.
Payloadi przeznaczone do nawigacji
Powiadomienie, które jedynie mówi
New Order
nie informuje aplikacji o tym, gdzie użytkownik powinien trafić. Należy dodać dane strukturalne obok widocznego tekstu:
{
"notification": {
"title": "New Order",
"body": "Order #12345 has been confirmed"
},
"data": {
"type": "order",
"orderId": "12345"
}
}
Funkcja routera może następnie powiązać type z ekranem:
function handleNotification(data: any) {
if (data.type === 'order') {
navigation.navigate(
'OrderDetails',
{
orderId: data.orderId,
},
);
}
}
Ponieważ obsługi powiadomień często działają poza komponentami, navigation w takich narzędziach to zazwyczaj referencja do nawigacji utworzona w korzeniu aplikacji. Pełniejsze omówienie routingu poprzez deep links znajduje się pod adresem routing push notification taps through deep links in React Native.
Rozpoznawanie zmian uprawnień po zainstalowaniu
Uprawnienia nie są stałe w czasie. Użytkownik może zezwolić na powiadomienia w poniedziałek:
Monday:
Notifications = ON
i wyłączyć je w ustawieniach systemu we środę:
Wednesday:
User opens Android SettingsNotifications = OFF
Aplikacja powinna to zauważyć. Notifee odczytuje obecne ustawienia bez żadnych pytań:
import notifee, {
AuthorizationStatus,
} from '@notifee/react-native';
export async function checkNotificationPermission() {
const settings =
await notifee.getNotificationSettings(); if (
settings.authorizationStatus ===
AuthorizationStatus.AUTHORIZED
) {
return true;
} return false;
}
Należy pamiętać, że ta weryfikacja zwraca false dla uprawnień tymczasowych; dodaj flagę PROVISIONAL, jeśli twoja aplikacja traktuje je jako aktywne. Uruchom ją ponownie, gdy aplikacja wróci na pierwszy plan.
Gdy użytkownik odmawia
Nie powtarzaj prośby. Zamiast tego pokaż jasny stan, na przykład baner:
Notifications are disabled
z akcją prowadzącą do ustawień:
Enable notifications from Settings
to receive important updates.[Open Settings]
Notifee oferuje interfejsy API, które umożliwiają bezpośrednie otwarcie ustawień powiadomień aplikacji. Zasady leżące u podstaw tego wszystkiego mieszczą się na jednej karcie:
Don't force
Don't spam
Explain the value
Give the user control
Centralizacja konfiguracji powiadomień
w kodzie produkcyjnym umieść wszystko związane z powiadomieniami w kilku dobrze zdefiniowanych modułach:
src/
│
├── services/
│ ├── notificationService.ts
│ └── firebaseMessaging.ts
│
├── navigation/
│ └── notificationNavigation.ts
│
├── screens/
│
└── App.tsx
Główna usługa:
// notificationService.ts
Ujawnia inicjalizator, który prosi o zgodę, zatrzymuje się, jeśli użytkownik odmówi, a w przeciwnym razie tworzy kanał domyślny:
import notifee, {
AndroidImportance,
AuthorizationStatus,
} from '@notifee/react-native';export async function initializeNotifications() {
const settings =
await notifee.requestPermission(); const authorized =
settings.authorizationStatus ===
AuthorizationStatus.AUTHORIZED ||
settings.authorizationStatus ===
AuthorizationStatus.PROVISIONAL; if (!authorized) {
return false;
} await notifee.createChannel({
id: 'default',
name: 'Default Notifications',
importance: AndroidImportance.DEFAULT,
}); return true;
}
Wywołanie go z komponentu jest proste:
useEffect(() => {
initializeNotifications();
}, []);
Uwaga: na pierwszym ekranie pojawia się to natychmiast, co powoduje problem z czasem opisany wcześniej. Lepiej wywołać to z przycisku zgody.
Część backendowa
Typowy model danych przechowuje użytkowników i ich urządzenia w oddzielnych tabelach, ponieważ jeden użytkownik może mieć kilka telefonów lub tabletów:
users
│
├── id
├── name
└── email
user_devices
│
├── id
├── user_id
├── device_id
├── fcm_token
├── platform
├── created_at
└── updated_at
Zapis rejestracji urządzenia może wyglądać w ten sposób:
{
"userId": 101,
"deviceId": "device-abc",
"fcmToken": "xxxxx",
"platform": "android"
}
Gdy występuje wydarzenie biznesowe, backend wyszukuje tokeny użytkownika i przesyła je przez FCM:
Order Created
│
▼
Backend
│
▼
Find user's FCM token
│
▼
Firebase Cloud Messaging
│
▼
User's phone
FCM oczekuje, że wiadomości będą wysyłane z zaufanego środowiska, takiego jak serwer aplikacji lub Cloud Functions, a nigdy z klienta, ponieważ wysyłanie wymaga uprawnień serwera.
Payloadi, które są skalowalne
Projektuj każdy payload tak, aby aplikacja rozumiała wydarzenie, a nie tylko tekst. Potwierdzenie płatności może zawierać:
{
"notification": {
"title": "Payment Successful",
"body": "Your payment of ₹999 was successful."
},
"data": {
"type": "payment",
"paymentId": "PAY12345",
"screen": "PaymentDetails"
}
}
Jeden switch na podstawie data.type obsługuje wtedy każdy rodzaj powiadomienia:
switch (data.type) {
case 'payment':
navigation.navigate(
'PaymentDetails',
{
paymentId: data.paymentId,
},
);
break;
case 'order':
navigation.navigate(
'OrderDetails',
{
orderId: data.orderId,
},
);
break;
}
Każdy nowy typ powiadomienia to nowy przypadek. Wartości danych FCM są ciągami znaków, więc liczby należy parsować wyraźnie.
Pełna architektura
Łącząc wszystkie elementy, konfiguracja w środowisku produkcyjnym wygląda tak:
┌──────────────────┐
│ Backend API │
└────────┬─────────┘
│
▼
┌──────────────────┐
│ Firebase / FCM │
└────────┬─────────┘
│
┌────────────┴────────────┐
│ │
▼ ▼
┌──────────────┐ ┌──────────────┐
│ Android │ │ iOS │
└──────┬───────┘ └──────┬───────┘
│ │
▼ ▼
Notification APNs / FCM
Channel │
│ │
└────────────┬────────────┘
▼
React Native App
│
▼
Navigation / UI
Na iOS FCM sam nie dostarcza wiadomości; przekazuje je za pośrednictwem usługi Apple Push Notification (APNs), dlatego konieczne jest skonfigurowanie uprawnień APNs.
Powszechne błędy
Zapytanie przy pierwszym uruchomieniu
To rozwiązanie na pierwszym ekranie, bez żadnego wyjaśnienia, jest najczęstszym błędem:
useEffect(() => {
requestNotificationPermission();
}, []);
Najpierw wyjaśnij wartość, a dopiero potem poproś o zgodę w odpowiedzi na działanie użytkownika.
Zapominanie o kanałach
Bez kanału powiadomienia nie pojawią się w systemie Android 8.0 i nowszych. Stwórz go przed wyświetleniem:
await notifee.createChannel({
id: 'messages',
name: 'Messages',
});
Oczekiwanie na pojawienie się powiadomień w tle
Słuchanie za pomocą
messaging().onMessage(...)
nie oznacza, że w każdym przypadku pojawi się widoczne powiadomienie systemowe. Aby zapewnić spójną prezentację w tle, sam wyświetl je za pomocą Notifee.
Ignowowanie zmian tokenów
Token zapisany raz potrafi nagle przestać działać. Użyj
messaging().onTokenRefresh(...)
i wysyłaj każdy nowy token do serwera.
Rегистrowanie obsługowników w tle w komponentach
Obsługa w tle powinna znajdować się w punkcie wejścia, poza drzewem React, jak pokazano wcześniej.
Ignowowanie późniejszych zmian uprawnień
Użytkownicy mogą w każdej chwili cofnąć uprawnienia lub zablokować pojedynczy kanał. Twoje sprawdzenia powinny uwzględniać każdy z tych stanów:
Granted
Denied
Changed externally
Channel blocked
App notifications disabled
Uciekanie wrażliwych danych
Tekst powiadomienia jest widoczny na ekranie blokady oraz dla każdego znajdującego się w pobliżu urządzenia. Zamiast tego
Your bank account ending in 1234 received ₹50,000.
wyślij coś neutralnego:
New account activity
Tap to view details.
i pokaż szczegóły dopiero po tym, jak użytkownik zaloguje się w aplikacji.
Listwa kontrolna testów
Zachowanie różni się w zależności od wersji systemu operacyjnego, stanu aplikacji i urządzenia, dlatego należy przeprowadzać testy systematycznie. Na Androidzie:
☐ Android 12
☐ Android 13+
☐ Permission granted
☐ Permission denied
☐ Permission disabled from Settings
☐ Channel enabled
☐ Channel disabled
☐ App foreground
☐ App background
☐ App killed
☐ Notification tapped
☐ Multiple notifications
Na iOS:
☐ Permission granted
☐ Permission denied
☐ Permission changed from Settings
☐ App foreground
☐ App background
☐ App terminated
☐ Notification tapped
☐ Badge
☐ Sound
☐ Deep linking
A dla integracji FCM w trybie end-to-end:
☐ Token generated
☐ Token stored on backend
☐ Token refresh handled
☐ Foreground message received
☐ Background message received
☐ Notification click handled
☐ Initial notification handled
Zarys przepływu w czasie rzeczywistym
Pełna sekwencja, od uruchomienia aplikacji przez dostawę do nawigacji, wygląda następująco:
APP START
│
▼
Check permissions
│
┌────────┴────────┐
│ │
Allowed Denied
│ │
▼ ▼
Initialize FCM Show explanation
│ │
▼ ▼
Get FCM token Request permission
│
▼
Send token to API
│
▼
App Running
│
┌──────┴──────┐
│ │
▼ ▼
Foreground Background
│ │
▼ ▼
onMessage FCM/APNs
│ │
▼ ▼
Notifee System UI
│ │
└──────┬──────┘
▼
User taps
│
▼
Read notification data
│
▼
Navigation
Uwierzytelnianie lokalne versus push oraz ich wspólne wykorzystanie
Dwa te typy powiadomień różnią się kilkoma przewidywalnymi aspektami:
- Zapłon: powiadomienia lokalne pochodzą z aplikacji lub urządzenia; powiadomienia push pochodzą z twojego backendu.
- Sieć: powiadomienia lokalne zazwyczaj działają offline; powiadomienia push wymagają połączenia.
- FCM: nie jest potrzebny dla powiadomień lokalnych; zazwyczaj jest wymagany dla powiadomień push.
- Zaplanowane przypomnienia: naturalnie pasują do powiadomień lokalnych; nie są typowym zastosowaniem powiadomień push.
- Zdarzenia serwera, aktualizacje zamówień, wiadomości w czacie: powiadomienia push są standardowym wyborem; powiadomienia lokalne mogą je obsłużyć tylko w ograniczony sposób.
- Biblioteki: Notifee dla powiadomień lokalnych; FCM w połączeniu z biblioteką do wyświetlania dla powiadomień push.
One również dobrze ze sobą współpracują. Typowa konfiguracja polega na tym, że FCM dostarcza wiadomość, twój kod ją przetwarza, a Notifee ją wyświetla:
FCM receives message
↓
React Native processes message
↓
Notifee displays native notification
Co powinni zobaczyć użytkownicy na każdym kroku
Podczas ręcznej weryfikacji warto wiedzieć, czego się spodziewać. W systemie Android 13+ pojawia się okno dialogowe:
Allow MyApp to send you notifications?
W ustawieniach powiadomień systemowych aplikacji kanały, które stworzyłeś, widnieją jako oddzielne przyciski:
MyApp
├── Messages
├── Orders
└── Promotions
W systemie iOS pojawia się prośba o uprawnienia, a dostarczone powiadomienia ukazują się w Centrum Powiadomień. W przypadku powiadomień typu push konsola Firebase może wysłać wiadomość testową bezpośrednio do tokena urządzenia, co jest najszybszym sposobem na odróżnienie błędów backendu od błędów klienta:
Firebase Console
↓
Cloud Messaging
↓
Test Message
↓
FCM Token
W końcu po kliknięciu w powiadomienie zamówienia:
Notification
↓
Order #12345
powinno się trafić na odpowiedni ekran:
Order Details Screen
Najlepsze praktyki w produkcji
Zrób tak, aby prośba o pozwolenie była wysyłana w odpowiednim momencie, dzięki czemu użytkownicy zrozumieją, dlaczego powiadomienia są przydatne, a nie tylko dlatego, że aplikacja się uruchomiła. Zachowaj logikę w jednej usłudze, zamiast ją powielać na różnych ekranach:
notificationService.ts
Rozdziel typy powiadomień na kanały lub kategorie, na przykład:
messages
orders
payments
security
marketing
Zaprojektuj treść przesyłanych danych do nawigacji, włączając w to odpowiednie pola:
type
id
screen
Ponadto:
- Powiąż tokeny z uwierzytelnionymi użytkownikami i urządzeniami, przechowuj je w bezpieczny sposób oraz zajmij się ich odnowieniem.
- Unikaj umieszczania poufnych informacji w tekście powiadomień, ponieważ są one widoczne poza aplikacją.
- Testuj na rzeczywistych urządzeniach, ponieważ emulatory i symulatory mogą zachowywać się inaczej.
Podsumowanie
Działający system powiadomień to znacznie więcej niż zwykły wywołanie funkcji
showNotification();
Jest to połączenie wszystkich tych aspektów:
Permissions
+
Notification Channels
+
FCM Tokens
+
Foreground Handling
+
Background Handling
+
Notification Clicks
+
Deep Linking
+
Backend Integration
Kluczem jest jasne podzielenie obowiązków. FCM dostarcza wiadomości. System operacyjny odpowiada za uprawnienia oraz prezentację w formacie natywnym. Notifee umożliwia React Native kontrołę nad prezentacją lokalną. Twój backend decyduje, kiedy i co wysłać oraz do kogo. Gdy postrzega się to jako jeden system, wygląda to w ten sposób:
┌─────────────────┐
│ Backend API │
└────────┬────────┘
│
▼
┌─────────────────┐
│ FCM │
└────────┬────────┘
│
┌────────┴────────┐
▼ ▼
┌───────────┐ ┌───────────┐
│ Android │ │ iOS │
│ Channel │ │ APNs │
└─────┬─────┘ └─────┬─────┘
│ │
└────────┬────────┘
▼
React Native
│
▼
Notifee
│
▼
Native UI
Gdy te granice są ustalone, powiadomienia zachowują się przewidywalnie zarówno na Androidzie, jak i iOS, a także w stanach pierwszego planu, tła oraz zakończenia działania.
Szybki przewodnik
Zażądaj uprawnienia na Androidzie:
await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS,
);
Zażądaj uprawnienia na iOS:
await notifee.requestPermission();
Stwórz kanał na Androidzie:
await notifee.createChannel({
id: 'messages',
name: 'Messages',
});
Wyświetl powiadomienie:
await notifee.displayNotification({
title: 'New Message',
body: 'You have a new message.',
android: {
channelId: 'messages',
},
});
Słuchaj wiadomości FCM w stanie pierwszego planu:
messaging().onMessage(async message => {
console.log(message);
});
Słuchaj zmian tokenów:
messaging().onTokenRefresh(token => {
console.log(token);
});
Zarządzanie otwieraniem powiadomień z tła:
messaging().onNotificationOpenedApp(message => {
console.log(message.data);
});
Literatura pokrewna
- Wiązanie modułu Turbo w aplikacji end-to-end z React Native Codegen — Definiowanie typizowanej specyfikacji, uruchamianie procesu codegen oraz implementacja modułu Turbo na iOS i Android przy użyciu metod synchronicznych, Promise, callback oraz event-emitter.
- Kierowanie kliknięciami w powiadomieniach przez deep links w React Native — Konfiguracja universal links i app links, łączenie Pusher Beams z aplikacją React Native oraz mapowanie adresów URL internetowych na ekrany natywne, aby kliknięcie w powiadomienie otwierało właściwe miejsce.
- Podłączanie modułu Turbo w aplikacji od początku do końca za pomocą React Native Codegen — Zdefiniuj typową specyfikację, uruchom proces generowania kodu i zaimplementuj moduł Turbo na iOS oraz Android przy użyciu metod synchronicznych, Promise, callback oraz event-emitter.
- Wewnątrz usługi powiadamiania wielu dostawców: kolejki, rozwiązania awaryjne, zaufanie — Przewodnik projektowy niezawodnej usługi powiadamień: przechowywanie danych uwierzytelniających, plan awaryjny dostawcy, ponawiane próby wysyłki przez BullMQ oraz listy oczekujących na obsługę, sprawiedliwość wobec użytkowników oraz weryfikacja podpisu webhooka.
- Wysyłanie i odbieranie SMS-ów w Node.js za pomocą Twilio Webhooks i Express — Stwórz mały serwer Express, który wysyła teksty przez Twilio, odbiera odpowiedzi za pośrednictwem webhooku udostępnionego przez ngrok i automatycznie odpowiada na nie za pomocą TwiML.