После публикации новой версии мобильного приложения push-уведомления перестали приходить всем пользователям или только части устройств. В консоли отправки сообщение выглядит успешным, но на экране ничего нет. Иногда уведомления работают на старой версии, debug-сборке или Android, а production-релиз на iOS молчит.

Такой сбой нельзя сводить к одной причине. Push проходит несколько этапов: приложение получает регистрацию устройства, передает ее вашему backend, сервер отправляет запрос в FCM или APNs, платформа доставляет сообщение, а операционная система и приложение решают, показать ли его. Обновление могло нарушить любой из этих этапов.

Сначала определите масштаб и границу сбоя

  • уведомления не приходят всем пользователям новой версии;
  • проблема только на Android или только на iOS;
  • не работает production, но тестовая сборка получает сообщения;
  • push не отображается только при открытом приложении;
  • обычные notification-сообщения работают, а data-сообщения нет;
  • уведомления не приходят после повторного входа или переустановки;
  • часть устройств получает сообщение с большой задержкой;
  • backend получает ответ об успешной отправке, но использует старую регистрацию.

Для диагностики выберите одно конкретное устройство. Зафиксируйте платформу, версию ОС, версию приложения, user id, installation id, текущий push token или регистрацию в замаскированном виде, время теста и message id ответа провайдера. Без этой связки легко сравнивать разные установки и получать противоречивые результаты.

Что чаще всего ломается после обновления

Новая регистрация устройства не попала на сервер

Registration token или идентификатор установки может измениться. Клиент должен получить актуальное значение и безопасно отправить его вашему backend вместе с пользователем, платформой, версией приложения и временем обновления. Если обработчик token refresh удален, переименован или запускается до авторизации API, сервер продолжает отправлять на старую регистрацию.

Релиз подключен к другому Firebase-проекту

В сборку мог попасть google-services.json или GoogleService-Info.plist от dev-окружения. Тогда приложение регистрируется в одном проекте, а backend отправляет через credentials другого. Проверьте project id, app id, package name или bundle id в итоговом артефакте, а не только в исходном репозитории.

В iOS изменились entitlement или APNs environment

После смены профиля подписи, target или bundle id релиз может потерять Push Notifications capability либо получить неподходящий aps-environment. APNs device token привязан к конкретному приложению и окружению. Sandbox-токен нельзя считать адресом production-приложения.

Разрешение на уведомления не запрошено или отключено

Обновление логики onboarding может пропустить запрос разрешения. На Android новых версий пользовательские уведомления также требуют runtime-разрешение, а на iOS статус мог остаться denied после прежнего отказа. При этом получение token и возможность показать alert — не одно и то же. Проверяйте текущий authorization status отдельно.

Сломан Android notification channel

Пользователь мог отключить канал, а приложение после обновления продолжает отправлять в старый channel id. Некоторые параметры созданного канала нельзя надежно изменить простым обновлением кода. Проверьте существование канала, importance, звук и соответствие channel id в payload и приложении. Не создавайте новый id без плана миграции настроек.

Изменился обработчик foreground или background

Когда приложение открыто, SDK или ваш код может передавать сообщение в callback без автоматического системного баннера. После рефакторинга обработчик перестает создавать локальное уведомление. Для background data-сообщений влияют ограничения ОС, приоритет, энергосбережение и допустимое время работы.

Backend неправильно очищает или выбирает регистрации

Новая версия может отправлять registration до user id, а сервер привязывает ее к гостю и затем не переносит к вошедшему аккаунту. Другая ошибка — хранить один token на пользователя: вход на новом телефоне перезаписывает старый, а logout одного устройства отключает все установки.

Пошаговая диагностика одного устройства

  1. Проверьте системное разрешение уведомлений, настройки конкретного канала и режимы фокусировки.
  2. Получите текущую регистрацию из приложения и убедитесь, что callback обновления действительно вызывается.
  3. Найдите эту регистрацию в backend и сравните user id, installation id, platform, app version и updated_at.
  4. Отправьте тестовое сообщение на конкретную регистрацию из доверенного серверного окружения.
  5. Сохраните ответ FCM или APNs: HTTP status, provider message id и код ошибки.
  6. Проверьте логи клиента для foreground и background отдельно.
  7. Сравните итоговую release-конфигурацию с dev: Firebase project, bundle или package id, entitlement и credentials.
  8. Повторите тест после новой регистрации, но не удаляйте старые записи до сохранения доказательств.

Успешный ответ сервера push-провайдера означает принятие запроса, а не гарантированный показ пользователю. Дальше учитываются актуальность регистрации, состояние устройства, заголовки, payload и политика операционной системы.

Правильная модель хранения push-регистраций

Храните регистрацию как отдельную установку приложения, а не как поле в таблице users. Один пользователь может иметь несколько телефонов, а одно устройство — последовательно использовать несколько аккаунтов.

  • installation_id — стабильный внутренний идентификатор установки, если ваша архитектура его использует;
  • user_id — текущая привязка к аккаунту или null до входа;
  • provider_registration — token или актуальный идентификатор, сохраненный защищенно;
  • platform, app_version, environment и locale;
  • permission_status и last_registered_at;
  • last_success_at, last_error_code и disabled_at.

На сервере нужен уникальный индекс для provider registration и безопасный upsert. При обновлении регистрации новая запись должна привязаться к той же установке, а старая — деактивироваться после подтверждения. Не публикуйте полные tokens в аналитике и обычных логах.

Регистрация на Android и iOS

Клиент должен получать актуальную регистрацию при запуске и обрабатывать ее обновление, после чего повторяемо отправлять данные на сервер. Если загрузка не удалась из-за сети или отсутствующей сессии, нужен последующий retry. Однократная отправка только во время onboarding ненадежна.

Для APNs приложение регистрируется при запуске и передает полученный device token серверу. Apple рекомендует не считать локально закешированный token вечным. Если используется FCM поверх APNs, проверьте и связь с APNs, и регистрацию FCM: успешный вызов одного слоя не доказывает исправность другого.

app launch or token callback read current notification permission get current provider registration POST registration + installation_id + app_version server validates environment and upserts record retry safely if network or authentication is unavailable

Как обрабатывать ответы FCM и APNs

Backend обязан читать ответ на каждую отправку. Недействительную или отозванную регистрацию нужно деактивировать, а временную ошибку — повторить по ограниченной политике backoff. Нельзя бесконечно отправлять на старые tokens: это искажает статистику и расходует ресурсы.

  • проверяйте, относится ли ошибка к регистрации или к некорректному payload;
  • удаляйте регистрацию только по однозначному сигналу провайдера;
  • для временных ошибок используйте retry с jitter и пределом попыток;
  • храните provider message id для трассировки;
  • не повторяйте просроченное уведомление после истечения его бизнес-смысла.

Payload, foreground и фоновые сообщения

Сравните payload старой и новой версии приложения. Изменение имени поля, deep link, channel id, category или типа сообщения может привести к тому, что push доставлен, но обработчик его отбрасывает. Добавьте версию payload и поддерживайте переходный период, пока значимая доля пользователей остается на старом релизе.

Тестируйте минимум четыре состояния: приложение открыто, свернуто, выгружено и устройство перезагружено. Notification и data payload могут вести себя по-разному, особенно при ограничениях фоновой работы. Не используйте silent push как гарантированную очередь фоновых задач.

Как безопасно восстановить уведомления после неудачного релиза

  1. Остановите массовые повторные рассылки и сохраните метрики до и после версии.
  2. Исправьте регистрацию, конфигурацию проекта или entitlement в новом релизе.
  3. Добавьте повторную синхронизацию регистрации при запуске приложения.
  4. Разверните backend, который принимает старый и новый формат в переходный период.
  5. Выпускайте обновление поэтапно и сравнивайте долю активных регистраций и доставку по версии.
  6. Не отправляйте заново транзакционные сообщения без проверки актуальности заказа, оплаты или кода доступа.

Проверка результата

  • чистая установка получает регистрацию и передает ее backend;
  • обновление поверх предыдущей версии синхронизирует актуальную регистрацию;
  • вход, выход и смена аккаунта корректно меняют привязку только текущей установки;
  • уведомления приходят на Android и iOS в foreground и background;
  • production и test используют разные, явно проверяемые окружения;
  • отказ в разрешении дает понятное состояние и не вызывает бесконечный prompt;
  • invalid registrations деактивируются, временные ошибки повторяются ограниченно;
  • deep links и действия уведомления работают на старой и новой схеме payload.

Типичные ошибки при исправлении

  • просить пользователя переустановить приложение, не проверив регистрацию на сервере;
  • хранить только один push token на аккаунт;
  • смешивать dev и production registrations в одной выборке;
  • считать успешный ответ FCM гарантией отображения уведомления;
  • регистрировать token только один раз при первом запуске;
  • логировать полные tokens и персональный payload;
  • массово повторять все пропущенные уведомления после ремонта.

Как предотвратить повторение проблемы

Добавьте release-checklist для push: итоговый Firebase project, package или bundle id, APNs capability, environment, разрешения, channel id, обновление регистрации и тест с production-like backend. Секреты и provider credentials должны проверяться на сервере, но не попадать в мобильный клиент.

Наблюдайте воронку по версии приложения: активные установки, свежие регистрации, принятые provider-запросы, ошибки, открытия и действия. Резкое падение сразу после rollout позволит остановить выпуск до того, как проблема затронет всех пользователей.

Когда нужна помощь с FCM и APNs

Если push перестали работать после обновления, я могу проверить release-конфигурацию, регистрацию клиента, таблицу устройств, отправку backend, ответы FCM и APNs, payload и обработчики приложения. Для оценки пришлите платформу, версии приложения и ОС, обезличенный фрагмент лога регистрации, ответ провайдера и описание различий между рабочей и проблемной сборкой без credentials и полных tokens.