Сповіщення в React Native: дозволи, канали та життєвий цикл FCM
Дізнайтеся, як дозволи, канали Android, токени FCM та обробники станів переднього плану, фонового режиму та завершення праці взаємодіють у системі сповіщень React Native з Notifee.
Джерела: React Native Notifications & Permissions: A Complete Guide for Android and iOS
Для користувача сповіщення — це один рядок тексту, наприклад "Ваш замовлення вже відправлено". Для додатку, який його надсилає, цей рядок залежить від дозволів під час роботи програми, каналів сповіщень Android, токенів пристрою, сервісу Firebase Cloud Messaging (FCM), фонових обробників та різниць між платформами Android та iOS. Помилка на будь-якому рівні призводить до того, що сповіщення так і не надходять або відкривається неправильний екран. У цьому посібнику розглядається кожен з цих рівнів за допомогою Notifee та React Native Firebase, щоб ви могли створити систему сповіщень, яка буде працювати передбачувано у кожному стані додатку.
Локальні сповіщення та сповіщення типу push
Локальні сповіщення
Локальне сповіщення створюється на пристрої самим додатком без участі сервера. Типовим прикладом є нагадування про зустріч:
Reminder
Your meeting starts in 10 minutes.
Додаток може відображати повідомлення або планувати їх відправку офлайн. Notifee надає один API для React Native, який працює через вбудовані системи сповіщень обох платформ.
Push-сповіщення
Push-сповіщення починається на сервері. Ваш бекенд передає повідомлення до FCM, FCM доставляє його на пристрій, а додаток React Native реагує на це:
Your Backend
│
▼
Firebase Cloud Messaging
│
▼
Android / iOS Device
│
▼
React Native Application
FCM розрізняє повідомлення-сповіщення (які система може відобразити для користувача) та даних-повідомлення (довільні набори ключів-значень для вашого коду), а також може адресувати як окремий пристрій, так і групу пристроїв через теми.
Дозволи є частиною стану вашого додатку
Встановлення додатку більше не означає, що він буде показувати сповіщення. З Android 13 пермісія POST_NOTIFICATIONS є динамічною, і додатки, призначені для Android 13 або новіших версій, повинні запитати про неї перед тим, як відображати щось. iOS завжди вимагав прямої авторизації користувача. Розглядайте цю пермісію як стан додатку, який може змінюватися, а не як одноразовий крок налаштування.
Розумний алгоритм спочатку перевіряє статус, пояснює переваги, а потім запитує; будь-яка відповідь дозволяє додатку продовжити роботу:
Application starts
│
▼
Check notification permission
│
├── Granted ───────► Continue
│
└── Not granted
│
▼
Explain the benefit
│
▼
Request permission
│
┌─────┴─────┐
▼ ▼
Granted Denied
│ │
▼ ▼
Notifications Continue
Встановлення бібліотек
Notifee відповідає за відображення сповіщень:
npm install @notifee/react-native
React Native Firebase надає основний модуль додатку та модуль для обміну повідомленнями через push:
npm install @react-native-firebase/app
npm install @react-native-firebase/messaging
У iOS після цього встановлюються нативні залежності CocoaPods:
cd ios
pod install
cd ..
Нативна конфігурація варіюється залежно від версії React Native та налаштувань проекту, а для push-сповіщень на iOS також потрібні облікові дані APNs у Firebase та підтримка push-функцій у Xcode. Дотримуйтеся актуальної документації кожної бібліотеки.
Відображення першого нативного сповіщення
Метою першого прикладу є сповіщення з такою назвою та текстом:
New Message
You have received a new message.
Спочатку імпортуйте Notifee разом із енумерацією важливості, яка використовується для каналів Android:
import notifee, {
AndroidImportance,
} from '@notifee/react-native';
Функція нижче виконує три дії по черзі: вона запитує дозвіл, створює (або повторно використовує) канал Android під назвою default, та відображає сповіщення на цьому каналі. Параметр pressAction з ідентифікатором default вказує Android на необхідність відкриття додатку після натискання на сповіщення; без нього натискання нічого не викликає.
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',
},
},
});
}
Багаторазове викликання createChannel є безпечним; існуючий канал просто повертає свій ідентифікатор. Канали є обов’язковими в Android 8.0 (API 26) та новіших версіях. Скористайтеся кнопкою для запуску функції:
<Button
title="Show Notification"
onPress={displayNotification}
/>
Запит на дозвіл Android 13
Повна назва дозволу в Android є такою:
android.permission.POST_NOTIFICATIONS
React Native робить його доступним як константу в PermissionsAndroid:
PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS
Цей дозвіл також потрібно оголосити за допомогою uses-permission у файлі AndroidManifest.xml, інакше запит буде мовчки відхилений. Почніть з імпортів:
import {
PermissionsAndroid,
Platform,
} from 'react-native';
Допоміжна функція негайно повертає true на iOS та в версіях Android нижче 13 (рівень API 33), де сповіщення дозволені за замовчуванням. В інших випадках вона відображає системне діалогове вікно та повідомляє, чи був результат 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;
}
Викликайте його з екрана:
const granted =
await requestAndroidNotificationPermission();
і робіть вибір на основі результату:
if (granted) {
console.log('Notifications allowed');
} else {
console.log('Notifications denied');
}
Android може повернути три значущі результати:
GRANTED
DENIED
NEVER_ASK_AGAIN
NEVER_ASK_AGAIN вимагає особливої уваги. Після кількох відмов система припиняє показувати діалог, і лише налаштування системи можуть змінити відповідь.
Запит у правильний момент
Показ системного запиту вже на першому екрані — це поширена та дорога помилка. Новий користувач, який одразу бачить
"Allow MyApp to send notifications?"
не має контексту та, ймовірно, відмовиться. Спочатку поясніть, наприклад, на екрані вітання:
Welcome to MyApp
потім покажіть картку з описом призначення сповіщень та кнопку:
Enable notifications to receive:
✓ New messages
✓ Order updates
✓ Important alerts[Enable Notifications]
Лише натискання на неї активує діалог операційної системи, тож запит відповідає на вибір користувача, а не перериває його роботу.
Дозволи в iOS з Notifee
У iOS користувач повинен прямо дозволити надсилання сповіщень. Notifee повідомляє про результат через AuthorizationStatus:
import notifee, {
AuthorizationStatus,
} from '@notifee/react-native';
Наведений нижче помічник вважає два статуси успішними. AUTHORIZED означає повні права. PROVISIONAL — це режим тиші iOS, у якому сповіщення надсилаються безпосередньо до Центру сповіщень, не перериваючи користувача, а він може пізніше їх залишити чи вимкнути. Усе інше вважається відмовою:
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;
}
Запит iOS може містити низку опцій:
Alerts
Sounds
Badges
CarPlay
Announcements
Provisional notifications
Прошіть лише те, що вам потрібно; наприклад, тимчасове авторизування повністю уникає запиту на дозвіл.
Одна функція дозволу для всього додатку
Замість розкидання перевірок по різних екранах використовуйте один модуль послуг:
src/
├── services/
│ └── notificationService.ts
├── screens/
├── components/
└── App.tsx
Файл послуги:
// notificationService.ts
Поєднує шляхи для обох платформ. На Android виконується перевірка версії та запит під час роботи програми; на iOS використовується Notifee, який приймає як авторизований, так і тимчасовий статус:
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
);
}
Потім екрани викликають одну функцію:
const enabled = await requestNotificationPermission();
і діють відповідно до результату:
if (enabled) {
console.log('Ready for notifications');
}
Останні версії Notifee також можуть запитувати POST_NOTIFICATIONS на Android 13 за допомогою requestPermission(); перевірте поточну документацію Notifee, якщо бажаєте один запит для обох платформ.
Канали сповіщень Android
У Android 8.0 були введені канали, щоб користувачі могли окремо керувати категоріями сповіщень. Додаток може мати кілька таких каналів:
MyApp
│
├── Messages
├── Orders
├── Promotions
└── Security
Кожен канал має свою важливість та звук. Зазвичай повідомлення мають високу важливість, тому вони з’являються:
const messageChannel = await notifee.createChannel({
id: 'messages',
name: 'Messages',
importance: AndroidImportance.HIGH,
});
Під час оновлення замовлень можна використовувати стандартний рівень:
const orderChannel = await notifee.createChannel({
id: 'orders',
name: 'Orders',
importance: AndroidImportance.DEFAULT,
});
Потім сповіщення вказує назву каналу, до якого воно належить. Виклик показаний у двох частинах: спочатку заголовок та тіло:
await notifee.displayNotification({
title: 'New Message',
body: 'You received a new message.',
а потім опції Android, які посилаються на ідентифікатор каналу, повернений функцією createChannel:
android: {
channelId: messageChannel,
},
});
Канали орієнтовані на користувачів, а не є лише налаштуваннями розробника. Користувачі можуть вимкнути або заблокувати канал у системних налаштуваннях, і після створення каналу додаток більше не може програмно змінювати його пріоритет; це рішення належить користувачеві. Обирайте ретельно або використовуйте новий ідентифікатор каналу пізніше.
Структура, яка добре працює в умовах продакшну, розділяє канали за призначенням:
notifications
│
├── messages
├── orders
├── payments
├── security
└── marketing
Окремий маркетинговий канал дозволяє користувачам вимикати рекламні повідомлення, не втрачаючи попереджень про безпеку.
Яке місце займає FCM
Для режиму push FCM знаходиться між вашим бекендом та кожним встановленим додатком:
┌─────────────────┐
│ Your Backend │
└────────┬────────┘
│
▼
┌─────────────────┐
│ FCM │
└────────┬────────┘
│
┌───────┴───────┐
▼ ▼
┌──────────┐ ┌──────────┐
│ Android │ │ iOS │
└──────────┘ └──────────┘
Отримання токена пристрою
Кожна інсталяція додатку може отримати токен реєстрації FCM, і ваш бекенд потребує цього токена для взаємодії з конкретним пристроєм. Імпортуйте модуль обміну повідомленнями:
import messaging from '@react-native-firebase/messaging';
та запитайте токен:
export async function getFCMToken() {
const token = await messaging().getToken(); console.log('FCM Token:', token); return token;
}
У справжньому додатку токен надсилається на ваш сервер відразу після його отримання:
const token = await messaging().getToken();
await fetch('https://api.example.com/device-token', {
method: 'POST', headers: {
'Content-Type': 'application/json',
}, body: JSON.stringify({
token,
}),
});
Потім бекенд зберігає токен разом із інформацією про увійшового користувача та пристрій:
User
│
├── userId
├── deviceId
└── fcmToken
Підтримка актуальності токенів
Токени змінюються, наприклад після переінсталяції чи очищення даних додатку. Слухайте події оновлення:
import messaging from '@react-native-firebase/messaging';
Слухач передає кожен новий токен, а функція повертає функцію для скасування підписки:
export function subscribeToTokenRefresh() {
return messaging().onTokenRefresh(async token => {
console.log('New FCM token:', token); await sendTokenToBackend(token);
});
}
Мінімальний помічник для завантаження, знову представлений у двох частинах, виконує 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,
}),
});
}
Власні рекомендації Firebase полягають у тому, щоб обробляти зміни токенів та надсилати нові значення на ваш сервер додатку щоразу, коли вони змінюються. З боку сервера також слід видаляти токени, які FCM вважає недійсними, щоб не накопичувалися застарілі пристрої.
Повідомлення на першому плані не відображаються автоматично
Найбільш заплутаним випадком є надходження повідомлення під час роботи додатку:
User
│
▼
React Native App
│
▼
FCM message
Прослуховуйте повідомлення на першому плані:
import messaging from '@react-native-firebase/messaging';
const unsubscribe = messaging().onMessage(
async remoteMessage => {
console.log('Foreground message:', remoteMessage);
},
);
Однак отримання повідомлення та відображення системного сповіщення — це окремі кроки. Коли додаток знаходиться на першому плані, FCM передає повідомлення у ваш код, але самостійно нічого не відображає. Щоб щось показати, передайте вміст до Notifee:
import notifee from '@notifee/react-native';
Наведений нижче обробник переконується, що канал існує, а потім відображає сповіщення; у разі, якщо повідомлення не має блоку notification (наприклад, повідомлення лише з даними), використовується стандартний текст:
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',
},
},
});
});
Ви контролюєте візуальне представлення, включаючи можливість пропуску його, якщо відповідний екран вже видимий.
Повідомлення на фоні та у стані завершення роботи
Коли додаток знаходиться на фоні, його поведінка змінюється. На Android сповіщення FCM потрапляють безпосередньо у системну панель, не викликаючи вашого обробника у фоновому режимі, а повідомлення, які поєднують сповіщення та дані, підкоряються власним правилам роботи на фоні. На iOS повідомлення лише з даними на фоні обробляються за принципом „найкращих зусиль“ та можуть бути затримані або обмежені системою.
React Native Firebase надає спеціалізований обробник для повідомлень на фоні:
import messaging from '@react-native-firebase/messaging';
messaging().setBackgroundMessageHandler(
async remoteMessage => {
console.log(
'Background message:',
remoteMessage,
);
},
);
Частою помилкою є реєстрація цього обробника всередині компонента React, як у наступному фрагменті:
function App() {
messaging().setBackgroundMessageHandler(...);
return <Navigation />;
}
Фонове пробудження може не завантажувати жодного компонента, тож обробник ніколи не існуватиме. Реєструйте його у точці входу:
// index.js
import messaging from '@react-native-firebase/messaging';messaging().setBackgroundMessageHandler(
async remoteMessage => {
console.log(
'Background message:',
remoteMessage,
);
},
);
і лише потім реєструйте кореневий компонент:
AppRegistry.registerComponent(
appName,
() => App,
);
Реакція на натискання сповіщень
Натискання зазвичай має переносити користувача в певне місце:
Notification
│
▼
User taps
│
▼
Application opens
│
▼
Order Details
Припустимо, що повідомлення містить ідентифікатор замовлення у своїх даних:
{
"orderId": "12345"
}
Для додатків, які відновлюються з фону, onNotificationOpenedApp викликається, коли користувач натискає на сповіщення. Колбек читає ідентифікатор з remoteMessage.data та може відтуди перейти:
messaging().onNotificationOpenedApp(
remoteMessage => {
const orderId =
remoteMessage.data?.orderId;
if (orderId) {
// Navigate to order details
console.log(
'Open order:',
orderId,
);
}
},
);
Коли додаток був повністю закритий
Якщо додаток було закрито, послідовність виглядає так:
App completely closed
│
▼
User receives notification
│
▼
User taps notification
│
▼
App launches
У цьому випадку onNotificationOpenedApp не спрацьовує. Натомість під час запуску потрібно один раз отримати сповіщення, яке запустило додаток:
const remoteMessage =
await messaging().getInitialNotification();
if (remoteMessage) {
const orderId =
remoteMessage.data?.orderId; console.log(
'App opened from notification:',
orderId,
);
}
Обидві методи охоплюють лише ті сповіщення, які відображав FCM. Клацання на сповіщеннях, які ви самі відобразили за допомогою Notifee, наприклад ті, що знаходяться у передньому плані, надходять через власні API подій Notifee (onForegroundEvent, onBackgroundEvent та їхній метод getInitialNotification). Обробка лише одного типу сповіщень є поширеною причиною помилок типу „клацання не має ефекту“.
Дані, призначені для навігації
Сповіщення, яке лише каже
New Order
, не повідомляє додатку, куди саме має перейти користувач. Додайте структуровані дані поруч із видимим текстом:
{
"notification": {
"title": "New Order",
"body": "Order #12345 has been confirmed"
},
"data": {
"type": "order",
"orderId": "12345"
}
}
Функція маршрутизатора може потім прив’язати type до екрана:
function handleNotification(data: any) {
if (data.type === 'order') {
navigation.navigate(
'OrderDetails',
{
orderId: data.orderId,
},
);
}
}
Оскільки обробники сповіщень часто виконуються поза компонентами, navigation у таких допоміжних функціях зазвичай є посиланням на навігацію, створеним у корені додатку. Для детального опису маршрутизації кліків через глибокі посилання дивіться маршрутизацію кліків на сповіщення через глибокі посилання в React Native.
Виявлення змін дозволів після встановлення
Дозволи не є стабільними з часом. Користувач може дозволити сповіщення у понеділок:
Monday:
Notifications = ON
а вимкнути їх у налаштуваннях системи у середу:
Wednesday:
User opens Android SettingsNotifications = OFF
Додаток повинен це помітити. Notifee читає поточні налаштування без запиту користувача:
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;
}
Зверніть увагу, що ця перевірка повертає false для тимчасового авторизування; додайте PROVISIONAL, якщо ваше додаток вважає його активним. Перезапустіть її, коли додаток повернеться на перший план.
Коли користувач відмовляється
Не повторюйте запит. Натомість покажіть чіткий стан, наприклад банер:
Notifications are disabled
з дією, яка веде до налаштувань:
Enable notifications from Settings
to receive important updates.[Open Settings]
Notifee пропонує API, які дозволяють безпосередньо відкрити налаштування сповіщень додатку. Принципи, що лежать в основі всього цього, можна описати на одній картці:
Don't force
Don't spam
Explain the value
Give the user control
Централізація налаштувань сповіщень
У продакшн-коді тримайте все, що стосується сповіщень, у кількох чітко визначених модулях:
src/
│
├── services/
│ ├── notificationService.ts
│ └── firebaseMessaging.ts
│
├── navigation/
│ └── notificationNavigation.ts
│
├── screens/
│
└── App.tsx
Основна служба:
// notificationService.ts
Він містить ініціалайзер, який запитує дозвіл користувача, зупиняється у разі відмови та інакше створює стандартний канал:
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;
}
Викликати його з компонента дуже просто:
useEffect(() => {
initializeNotifications();
}, []);
Увага: на першому екрані це відбувається миттєво, що призводить до проблеми з таймінгом, описаної раніше. Краще викликати його з кнопки добровільної участі.
Бекенд
Типова модель даних зберігає користувачів та їхні пристрої в окремих таблицях, оскільки один користувач може мати кілька телефонів чи планшетів:
users
│
├── id
├── name
└── email
user_devices
│
├── id
├── user_id
├── device_id
├── fcm_token
├── platform
├── created_at
└── updated_at
Запис про реєстрацію пристрою може виглядати так:
{
"userId": 101,
"deviceId": "device-abc",
"fcmToken": "xxxxx",
"platform": "android"
}
Коли відбувається бізнес-захід, бекенд знаходить токени користувача та надсилає їх через FCM:
Order Created
│
▼
Backend
│
▼
Find user's FCM token
│
▼
Firebase Cloud Messaging
│
▼
User's phone
FCM очікує, що повідомлення надсилаються з надійного середовища, такого як ваш сервер додатку або Cloud Functions, ніколи не з клієнта, оскільки для надсилання потрібні облікові дані сервера.
Полезні навантаження, які підлаштовуються
Проектуйте кожне полезне навантаження так, щоб додаток розумів подію, а не лише текст. Підтвердження оплати може містити:
{
"notification": {
"title": "Payment Successful",
"body": "Your payment of ₹999 was successful."
},
"data": {
"type": "payment",
"paymentId": "PAY12345",
"screen": "PaymentDetails"
}
}
Один switch на основі data.type обробляє кожен тип сповіщення:
switch (data.type) {
case 'payment':
navigation.navigate(
'PaymentDetails',
{
paymentId: data.paymentId,
},
);
break;
case 'order':
navigation.navigate(
'OrderDetails',
{
orderId: data.orderId,
},
);
break;
}
Кожен новий тип сповіщення — це ще один випадок. Значення даних FCM є рядками, тому числа потрібно обробляти явно.
Повна архітектура
Якщо об’єднати всі елементи, продакшн-налаштування виглядають так:
┌──────────────────┐
│ Backend API │
└────────┬─────────┘
│
▼
┌──────────────────┐
│ Firebase / FCM │
└────────┬─────────┘
│
┌────────────┴────────────┐
│ │
▼ ▼
┌──────────────┐ ┌──────────────┐
│ Android │ │ iOS │
└──────┬───────┘ └──────┬───────┘
│ │
▼ ▼
Notification APNs / FCM
Channel │
│ │
└────────────┬────────────┘
▼
React Native App
│
▼
Navigation / UI
У iOS FCM сам не доставляє повідомлення; він передає їх через сервіс Apple Push Notification (APNs), тому необхідно налаштувати облікові дані APNs.
Поширені помилки
Запит на дозвіл під час першого запуску
Ця схема на першому екрані без будь-яких пояснень є найпоширенішою помилкою:
useEffect(() => {
requestNotificationPermission();
}, []);
Спочатку поясніть значення та попросіть дозвіл у відповідь на дію користувача.
Забування про канали
Без каналу сповіщення не з’являтимуться в Android 8.0 та новіших версіях. Створіть його перед відображенням:
await notifee.createChannel({
id: 'messages',
name: 'Messages',
});
Очікування відображення сповіщень на першому плані
Використання функції
messaging().onMessage(...)
не означає, що у кожному випадку з’явиться видиме системне сповіщення. Для послідовного відображення на першому плані використовуйте Notifee.
Ігнорування змін токенів
Токен, збережений один раз, починає працювати некоректно. Використовуйте
messaging().onTokenRefresh(...)
та надсилайте кожен новий токен на сервер.
Реєстрація обробників у фоновому режимі в компонентах
Обробник фонових завдань має знаходитися у точці входу, поза деревом React, як було показано раніше.
Ігнорування подальших змін дозволів
Користувачі можуть скасувати дозволи або заблокувати окремий канал у будь-який час. Ваші перевірки мають враховувати кожен з цих станів:
Granted
Denied
Changed externally
Channel blocked
App notifications disabled
Витік конфіденційних даних
Текст сповіщення видимий на екрані блокування та для всіх, хто знаходиться поруч із пристроєм. Замість того, щоб
Your bank account ending in 1234 received ₹50,000.
надіслати щось нейтральне:
New account activity
Tap to view details.
і показувати деталі лише після того, як користувач автентифікується всередині додатка.
Чек-лист тестування
Поведінка змінюється залежно від версії ОС, стану додатка та пристрою, тому необхідно тестувати систематично. На Android:
☐ 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
На iOS:
☐ Permission granted
☐ Permission denied
☐ Permission changed from Settings
☐ App foreground
☐ App background
☐ App terminated
☐ Notification tapped
☐ Badge
☐ Sound
☐ Deep linking
А для інтеграції FCM в режимі «від початку до кінця»:
☐ Token generated
☐ Token stored on backend
☐ Token refresh handled
☐ Foreground message received
☐ Background message received
☐ Notification click handled
☐ Initial notification handled
Огляд потоку виконання
Повна послідовність, від запуску додатка через доставку повідомлень до навігації, виглядає так:
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
Локальні повідомлення проти push-повідомлень та їхнє спільне використання
Ці два типи повідомлень відрізняються кількома очевидними аспектами:
- Джерело: локальні повідомлення надходять від додатка чи пристрою; push-повідомлення — від вашого бекенду.
- Мережа: локальні повідомлення зазвичай працюють офлайн; push-повідомлення вимагають підключення до мережі.
- FCM: не потрібен для локальних повідомлень; зазвичай необхідний для push-повідомлень.
- Заплановані нагадування: ідеально підходять для локальних повідомлень; не є типовим використанням push-повідомлень.
- Події сервера, оновлення замовлень, повідомлення в чаті: push-повідомлення є стандартним вибором; локальні повідомлення можуть їх обробляти лише обмеженим чином.
- Бібліотеки: Notifee для локальних повідомлень; FCM разом із бібліотекою для відображення для push-повідомлень.
Вони також добре поєднуються. Типова конфігурація дозволяє FCM доставляти повідомлення, ваш код обробляти його, а Notifee відображати:
FCM receives message
↓
React Native processes message
↓
Notifee displays native notification
Що мають бачити користувачі на кожному етапі
Для ручної перевірки потрібно знати, чого очікувати. У Android 13+ з’являється системне діалогове вікно:
Allow MyApp to send you notifications?
У налаштуваннях системних сповіщень додатку канали, які ви створили, відображаються як окремі перемикачі:
MyApp
├── Messages
├── Orders
└── Promotions
У iOS з’являється запит на авторизацію від системи, а надіслані сповіщення з’являються у Центрі сповіщень. Для пуш-сповіщень консоль Firebase може надіслати тестове повідомлення безпосередньо на токен пристрою, що є найшвидшим способом відокремити проблеми серверної частини від проблем клієнтської частини:
Firebase Console
↓
Cloud Messaging
↓
Test Message
↓
FCM Token
Нарешті, при натисканні на сповіщення про замовлення:
Notification
↓
Order #12345
слід потрапити на відповідний екран:
Order Details Screen
Найкращі практики для продакшну
Визначте правильний момент для запиту на дозвіл, щоб користувачі розуміли, чому сповіщення є корисними, а не просто тому, що додаток запустився. Зберігайте логіку в одному сервісі, а не дублюйте її на різних екранах:
notificationService.ts
Розділіть типи сповіщень на канали чи категорії, наприклад:
messages
orders
payments
security
marketing
Розробіть структуру даних для навігації, включаючи за потреби такі поля:
type
id
screen
Крім того:
- Пов’язуйте токени з автентифікованими користувачами та пристроями, зберігайте їх у безпеці та керуйте процесом оновлення.
- Уникайте використання конфіденційної інформації у тексті сповіщень, оскільки він є видимим поза межами додатку.
- Тестуйте на справжніх пристроях, адже емулятори та симулятори можуть поводитися по-різному.
Підсумок
Функціональна система сповіщень — це значно більше, ніж просто один виклик на кшталт
showNotification();
Це поєднання всіх цих аспектів:
Permissions
+
Notification Channels
+
FCM Tokens
+
Foreground Handling
+
Background Handling
+
Notification Clicks
+
Deep Linking
+
Backend Integration
Ключовим моментом є чітке розподілення обов’язків. FCM доставляє повідомлення. Операційна система відповідає за дозволи та нативне відображення. Notifee надає React Native контроль над локальним відображенням. Ваш бекенд вирішує, коли та що надсилати, а також кому. Якщо розглядати це як єдину систему, вона виглядає так:
┌─────────────────┐
│ Backend API │
└────────┬────────┘
│
▼
┌─────────────────┐
│ FCM │
└────────┬────────┘
│
┌────────┴────────┐
▼ ▼
┌───────────┐ ┌───────────┐
│ Android │ │ iOS │
│ Channel │ │ APNs │
└─────┬─────┘ └─────┬─────┘
│ │
└────────┬────────┘
▼
React Native
│
▼
Notifee
│
▼
Native UI
За наявності таких меж повідомлення поводяться прогнозовано як на Android, так і на iOS, у режимах фону, переднього плану та після завершення роботи.
Швидкий посібник
Запитайте дозвіл у Android:
await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS,
);
Запитайте дозвіл у iOS:
await notifee.requestPermission();
Створіть канал у Android:
await notifee.createChannel({
id: 'messages',
name: 'Messages',
});
Відобразіть повідомлення:
await notifee.displayNotification({
title: 'New Message',
body: 'You have a new message.',
android: {
channelId: 'messages',
},
});
Прослуховуйте повідомлення FCM у режимі переднього плану:
messaging().onMessage(async message => {
console.log(message);
});
Прослуховуйте зміни токенів:
messaging().onTokenRefresh(token => {
console.log(token);
});
Обробка сповіщень, які відкриваються з фону:
messaging().onNotificationOpenedApp(message => {
console.log(message.data);
});