Notification Service Extension — как устроена обработка уведомлений

Автор: IT Sectr Опубликовано: 2026-06-16 Время чтения: 9 мин

Notification Service Extension — это расширение iOS, которое перехватывает push-уведомление сразу после получения, но до отображения пользователю. Расширение может расшифровать зашифрованный payload, загрузить и прикрепить медиафайл, изменить текст или заголовок уведомления в реальном времени. По данным Apple Developer Documentation (2025), для активации расширения сервер должен отправить ключ mutable-content:1 в атрибутах уведомления — это единственное условие запуска UNNotificationServiceExtension.

Главное

  • Notification Service Extension модифицирует push-уведомление до его отображения пользователю на устройстве iOS
  • Для активации расширения требуется параметр mutable-content:1 в payload уведомления от сервера
  • Основной протокол — UNNotificationServiceExtension с методами didReceive(_:withContentHandler:) и serviceExtensionTimeWillExpire()
  • Расширение может загружать медиавложения из сети и прикреплять их к уведомлению через UNNotificationAttachment
  • Время выполнения ограничено — система выделяет около 30 секунд на полную обработку одного уведомления

Что такое Notification Service Extension

Notification Service Extension — это app extension в iOS, которое перехватывает входящее push-уведомление на стороне устройства и позволяет изменить его содержимое до того, как пользователь его увидит. Это единственный тип расширения уведомлений, который работает с контентом, а не с отображением.

Ключевое отличие от Notification Content Extension: Service Extension работает до показа уведомления и может изменить заголовок, тело, саундфайл и вложения. Content Extension работает после показа и управляет только визуальным представлением готового уведомления. Эти два расширения могут работать вместе: Service Extension загружает изображение, а Content Extension отображает его в кастомном интерфейсе.

Расширение активируется автоматически при получении push-уведомления с атрибутом mutable-content:1 в aps-словаре. iOS запускает расширение в фоновом режиме, передаёт ему оригинальный UNNotificationRequest и ожидает модифицированную версию для отображения.

Обработка push-уведомлений на лету

UNNotificationServiceExtension получает полный UNNotificationRequest с оригинальным содержимым. Расширение может модифицировать любые поля UNNotificationContent: title, subtitle, body, userInfo, attachments и sound. Изменения применяются до того, как уведомление будет показано.

Типовые сценарии использования

Расшифровка контента — если push-уведомление содержит зашифрованный payload, расширение расшифровывает его до показа. Загрузка медиа — прикрепление изображения или видео к уведомлению. Локализация — адаптация текста уведомления под региональные настройки устройства. Обогащение данных — добавление дополнительной информации из локального хранилища или кэша.

По данным Apple, наиболее частый сценарий среди приложений — загрузка изображений для богатых медиа-уведомлений. Сервер отправляет URL изображения в payload, расширение скачивает его во временную директорию и создаёт UNNotificationAttachment, который система отображает в стандартном или кастомном интерфейсе.

Модификация текста и заголовка

Расширение может полностью переписать text уведомления, заменить заголовок или добавить subtitle. Например, приложение мессенджера может получить зашифрованное уведомление, расшифровать его в расширении и отобразить читаемый текст. Или новостное приложение может добавить категорию новости в сабтайтл перед показом.

swift
override func didReceive(
    _ request: UNNotificationRequest,
    withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
) {
    let content = request.content.mutableCopy()
        as! UNMutableNotificationContent
    if let imageURL = content.userInfo["media-url"]
        as? String {
        downloadAndAttach(imageURL: imageURL,
            content: content,
            handler: contentHandler)
    }
}

Протокол UNNotificationServiceExtension

UNNotificationServiceExtension — базовый класс, от которого наследуется Service Extension. Класс определяет два метода жизненного цикла: didReceive(_:withContentHandler:) — основной метод обработки, и serviceExtensionTimeWillExpire() — обработчик истечения времени.

Метод didReceive:withContentHandler:

didReceive(_:withContentHandler:) вызывается при получении уведомления. Расширение получает UNNotificationRequest и замыкание contentHandler, которое нужно вызвать с модифицированным UNMutableNotificationContent. Расширение обязано вызвать contentHandler — если этого не сделать, iOS покажет оригинальное уведомление после истечения таймаута.

Важно: расширение может обрабатывать только одно уведомление за раз. Если приходят несколько уведомлений одновременно, iOS создаёт отдельные экземпляры расширения для каждого. Нельзя использовать глобальное состояние для последовательной обработки.

Метод serviceExtensionTimeWillExpire:

serviceExtensionTimeWillExpire() вызывается системой, когда оставшееся время выполнения подходит к концу. В этом методе нужно немедленно вызвать contentHandler с тем контентом, который готов на данный момент — даже если медиафайл ещё не загрузился. Если не вызвать contentHandler в этом методе, iOS покажет оригинальное уведомление.

Рекомендуется сохранять в этом методе минимально приемлемый контент — например, уведомление с текстом и заголовком, но без изображения, загрузка которого не завершилась вовремя.

Шифрование и медиавложения

UNNotificationAttachment — объект, создаваемый расширением для прикрепления медиафайла к уведомлению. Расширение загружает файл из сети, сохраняет его во временную директорию и создаёт UNNotificationAttachment с указанием типа контента.

Создание вложения из загруженного файла

UNNotificationAttachment создаётся с помощью инициализатора init(identifier:url:options:). URL должен указывать на локальный файл во временной директории, доступной для расширения. После создания attachment добавляется в массив attachments UNMutableNotificationContent.

Apple рекомендует использовать URLSession с фоновой конфигурацией для загрузки — при использовании стандартной URLSession загрузка блокирует поток и расходует время из 30-секундного лимита. Фоновая URLSession продолжает загрузку даже при завершении расширения, а результат может быть использован при следующем запуске.

Расшифровка зашифрованного payload

Если сервер отправляет зашифрованное уведомление, расширение должно расшифровать payload перед вызовом contentHandler. Расшифровка обычно включает запрос ключа из Keychain или App Group, дешифрование через CommonCrypto и замену body или userInfo уведомления. При ошибке расшифровки следует вызвать contentHandler с оригинальным содержимым — чтобы пользователь хотя бы видел, что уведомление пришло, пусть и нечитаемое.

swift
func downloadAndAttach(
    imageURL: String,
    content: UNMutableNotificationContent,
    handler: @escaping (UNNotificationContent) -> Void
) {
    let task = URLSession.shared.dataTask(with:
        URL(string: imageURL)!) { data, _, _ in
        let url = FileManager.default
            .temporaryDirectory
            .appendingPathComponent("image.jpg")
        try? data?.write(to: url)
        let attachment = try? UNNotificationAttachment(
            identifier: "image", url: url)
        content.attachments = [attachment].compactMap { $0 }
        handler(content)
    }
    task.resume()
}

Таймауты и fallback-механизмы

Notification Service Extension работает в жёстких временных рамках. iOS выделяет фиксированное время на выполнение — примерно 30 секунд с момента активации. Если расширение не вызвало contentHandler за это время, система принудительно завершает процесс и показывает оригинальное уведомление без изменений.

Стратегия graceful degradation

Рекомендуется реализовать многоуровневый fallback: сначала попытаться загрузить медиа, при успехе — вызвать contentHandler с полным контентом; при неудаче — вызвать contentHandler с текстом, но без медиа; при критической ошибке — передать оригинальное содержимое. Такой подход гарантирует, что пользователь всегда увидит уведомление, а не пустой экран.

По данным Apple, наиболее частой причиной таймаутов является загрузка больших медиафайлов при медленном соединении. Для снижения риска рекомендуется оптимизировать размер изображений на сервере — отправлять превью до 300 КБ вместо полного разрешения. Полноразмерные изображения стоит загружать уже при открытии приложения.

Мониторинг производительности

Для отслеживания таймаутов и ошибок расширения можно использовать os_log для записи диагностических сообщений в Unified Logging System. Хотя прямое логирование в файл в расширении затруднено, os_log позволяет анализировать производительность через Console.app на устройстве разработчика. Apple рекомендует добавлять метрики на каждый вызов didReceive — время загрузки, размер файла, результат операции.

swift
override func serviceExtensionTimeWillExpire() {
    let fallback = bestEffortContent as?
        UNMutableNotificationContent
        ?? request.content.mutableCopy()
        as! UNMutableNotificationContent
    contentHandler(fallback)
}

Часто задаваемые вопросы

Как сервер активирует Notification Service Extension?

Сервер добавляет ключ mutable-content:1 в aps-словарь push-уведомления. Без этого параметра система игнорирует расширение и показывает стандартное уведомление.

Можно ли использовать расширение без mutable-content?

Нет. mutable-content:1 — обязательное условие активации Service Extension. Если ключ отсутствует или установлен в 0, уведомление показывается без вызова расширения.

Что происходит при превышении 30-секундного лимита?

iOS принудительно завершает расширение и показывает оригинальное уведомление без изменений. Чтобы избежать этого, реализуйте serviceExtensionTimeWillExpire() с минимально приемлемым контентом.

Как передать ключи шифрования в расширение?

Через App Group (общий UserDefaults или файл) или Keychain с общим доступом между приложением и расширением. Прямая передача ключей в payload уведомления небезопасна.

Сколько медиафайлов можно прикрепить в Service Extension?

До 4 вложений на одно уведомление, каждое до 50 МБ. Суммарный размер вложений влияет на время загрузки — чем больше файлов, тем выше риск таймаута.

Итоги

  • Notification Service Extension модифицирует push-уведомления на устройстве до их отображения, изменяя текст, заголовок и медиавложения
  • Для активации необходим ключ mutable-content:1 в aps-словаре — без него расширение не запускается
  • Основной протокол UNNotificationServiceExtension определяет методы didReceive и serviceExtensionTimeWillExpire для обработки
  • Расширение может загружать медиа из сети, расшифровывать payload и добавлять до 4 вложений к уведомлению
  • Время выполнения ограничено ~30 секундами — при превышении iOS показывает оригинальное уведомление
  • Рекомендуется стратегия graceful degradation с многоуровневым fallback для предотвращения пустых уведомлений

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также