Push-уведомления кажутся простыми, пока не начнёшь разбираться, почему на части устройств они не приходят, а нажатие открывает не тот экран. На собесе спрашивают всю цепочку: кто доставляет, как получить токен, как обработать уведомление в разных состояниях приложения.
Кто доставляет
Приложение не держит своё соединение с сервером ради пушей. Доставку делает платформа:
- APNs (Apple Push Notification service) на iOS;
- FCM (Firebase Cloud Messaging) на Android. FCM умеет отправлять и на iOS, пересылая через APNs.
Цепочка: приложение получает токен устройства у платформы, отправляет его на свой сервер, сервер шлёт уведомление в APNs или FCM с этим токеном, платформа доставляет на устройство. Expo предлагает свою прослойку: Expo Push Service с токеном вида ExponentPushToken[...], который сам ходит в APNs и FCM.
Разрешения
- iOS: без явного согласия пользователя уведомления не показываются. Запрос показывается системой один раз; отказ можно изменить только в настройках. Поэтому сначала объясняют пользу своим экраном, а потом просят разрешение (pre-permission).
- Android 13+: нужно разрешение
POST_NOTIFICATIONS, тоже запрашивается в рантайме. На старых версиях уведомления разрешены по умолчанию. - Android каналы (8+): каждое уведомление принадлежит каналу со своей важностью и звуком. Пользователь может отключить отдельный канал.
Токен
Токен может меняться: переустановка, восстановление из бэкапа, обновление со стороны сервиса. Поэтому токен отправляют на сервер при каждом запуске (или по событию обновления токена) и привязывают к пользователю. При выходе из аккаунта токен отвязывают, иначе уведомления чужого аккаунта придут на устройство. Сервер удаляет токены, на которые APNs или FCM ответили «недействителен».
Виды сообщений
- Уведомление (notification): заголовок и текст. Когда приложение в фоне или закрыто, система показывает его сама.
- Данные (data-only, silent): только полезная нагрузка, без показа. Позволяют приложению проснуться и, например, синхронизировать данные. Доставка не гарантирована: iOS ограничивает фоновые пробуждения, Android может откладывать в режиме энергосбережения.
Три состояния приложения
| Состояние | Что происходит | Как обработать |
|---|---|---|
| Foreground | система по умолчанию не показывает баннер | решить самим: показать свой баннер или включить показ (обработчик setNotificationHandler в expo-notifications) |
| Background | система показывает уведомление | при нажатии приложение выходит на передний план, приходит событие нажатия |
| Закрыто (killed) | система показывает уведомление | при нажатии приложение стартует, данные уведомления получают при запуске (initial notification) |
Частая ошибка: обработать нажатие только в фоне и забыть холодный старт. Тогда уведомление открывает главный экран вместо нужного.
Навигация по нажатию
В полезную нагрузку кладут адрес экрана или тип и id, а не готовый объект: {"type": "order", "id": "42"} или {"url": "myapp://order/42"}. Обработчик нажатия переводит это в навигацию. Если навигатор ещё не готов (холодный старт), переход откладывают до готовности NavigationContainer. Если экран требует входа, сначала авторизация, потом переход.
Библиотеки
expo-notifications: разрешения, токены (Expo и нативные), локальные уведомления, обработчики.@react-native-firebase/messaging: FCM, фоновые обработчики.- Notifee: богатые локальные уведомления (каналы, действия, прогресс).
Частые ошибки
- Не запросить разрешение на Android 13+.
- Не обновлять токен на сервере и не отвязывать при выходе.
- Не обработать холодный старт по уведомлению.
- Полагаться на data-only сообщения для критичной логики.
- Тестировать только на симуляторе: доставка пушей проверяется на реальных устройствах (симулятор iOS поддерживает только ограниченные сценарии).
Что спросят на собесе
- Как доставляется push: APNs, FCM, токен.
- Разрешения на iOS и Android 13+, каналы.
- Как обработать уведомление в foreground, background и при закрытом приложении.
- Как по нажатию открыть нужный экран.
- Что делать с токеном при выходе пользователя.