Notification Service Extension — як влаштована обробка push-сповіщень

Автор: 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 — це розширення застосунку в 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, який система відображає в стандартному або кастомному інтерфейсі.

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

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

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 має вказувати на локальний файл у тимчасовій директорії, доступній для розширення. Після створення вкладення додається до масиву 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 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

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