Реализация Notification Service Extension для iOS
Notification Service Extension — отдельный таргет в Xcode, который перехватывает входящее push-уведомление до его показа пользователю. В этом окне (не более 30 секунд) можно изменить заголовок, тело, добавить media attachment или расшифровать payload. Если Extension не успевает или падает — показывается оригинальное уведомление.
Для чего используется на практике
Media attachments. Добавляем изображение к push-уведомлению. Сервер отправляет URL в payload (mutable-content: 1 обязателен). Extension скачивает изображение через URLSession, сохраняет во временную директорию, создаёт UNNotificationAttachment и передаёт через contentHandler. Без Extension attachments в push не работают.
Сквозное шифрование. Payload приходит зашифрованным, Extension расшифровывает с ключом из Keychain и подставляет читаемый текст. Без этого push-уведомления в e2e-мессенджерах показывали бы «Новое сообщение» без содержимого.
Аналитика доставки. В Extension делаем fire-and-forget запрос на backend при получении уведомления — фиксируем delivered событие. UIApplicationDelegate.userNotificationCenter(_:didReceive:) срабатывает только при тапе, Extension — при доставке.
Технические детали
Extension живёт в отдельном process, не имеет доступа к данным основного приложения напрямую. Для шаринга данных (например, ключи шифрования) используем App Groups: UserDefaults(suiteName: "group.com.example.app") и FileManager с контейнером group'ы.
Keychain Sharing: kSecAttrAccessGroup с group identifier'ом позволяет Extension читать секреты, записанные основным приложением.
Таймаут 30 секунд — жёсткий. Если скачиваем изображение, нужен таймаут на URLSession меньше 30 секунд с fallback: если не успели — показываем уведомление без attachment, не фейлим Extension. URLSessionConfiguration.default.timeoutIntervalForRequest = 20.
override func didReceive(_ request: UNNotificationRequest,
withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
// скачиваем attachment, при ошибке — contentHandler(bestAttemptContent!)
}
override func serviceExtensionTimeWillExpire() {
// вызывается за мгновение до таймаута — показываем что есть
if let contentHandler, let content = bestAttemptContent {
contentHandler(content)
}
}
Что входит в работу
- Создание и настройка таргета Notification Service Extension в Xcode
- Реализация логики: attachment, расшифровка или аналитика
- Настройка App Groups для шаринга данных с основным приложением
- Тестирование на реальном устройстве (Extension не работает в симуляторе для push от APNs)
- Entitlements и provisioning profiles для Extension таргета
Сроки
Реализация одного сценария (например, attachment): 1 день. С шифрованием, App Groups и несколькими логиками обработки: 2–3 дня. Стоимость рассчитывается индивидуально.







