Notification Service Extension — це розширення iOS, яке перехоплює push-сповіщення одразу після отримання, але до відображення користувачеві. Розширення може розшифрувати зашифрований payload, завантажити та прикріпити медіафайл, змінити текст або заголовок сповіщення в реальному часі. За даними Apple Developer Documentation (2025), для активації розширення сервер має надіслати ключ mutable-content:1 в атрибутах сповіщення — це єдина умова запуску UNNotificationServiceExtension.
Головне
Notification Service Extension — це розширення застосунку в iOS, яке перехоплює вхідне push-сповіщення на стороні пристрою та дозволяє змінити його вміст до того, як користувач його побачить. Це єдиний тип розширення сповіщень, який працює з вмістом, а не з відображенням.
Ключова відмінність від Notification Content Extension: Service Extension працює до показу сповіщення та може змінити заголовок, тіло, звуковий файл і вкладення. Content Extension працює після показу та керує лише візуальним представленням готового сповіщення. Ці два розширення можуть працювати разом: Service Extension завантажує зображення, а Content Extension відображає його в кастомному інтерфейсі.
Розширення активується автоматично при отриманні push-сповіщення з атрибутом mutable-content:1 у aps-словнику. iOS запускає розширення у фоновому режимі, передає йому оригінальний UNNotificationRequest та очікує модифіковану версію для відображення.
UNNotificationServiceExtension отримує повний UNNotificationRequest з оригінальним вмістом. Розширення може модифікувати будь-які поля UNNotificationContent: title, subtitle, body, userInfo, attachments та sound. Зміни застосовуються до того, як сповіщення буде показано.
Розшифрування вмісту — якщо push-сповіщення містить зашифрований payload, розширення розшифровує його до показу. Завантаження медіа — прикріплення зображення або відео до сповіщення. Локалізація — адаптація тексту сповіщення під регіональні налаштування пристрою. Збагачення даних — додавання додаткової інформації з локального сховища або кешу.
За даними Apple, найпоширеніший сценарій серед застосунків — завантаження зображень для багатих медіа-сповіщень. Сервер надсилає URL зображення в payload, розширення завантажує його в тимчасову директорію та створює UNNotificationAttachment, який система відображає в стандартному або кастомному інтерфейсі.
Розширення може повністю переписати текст сповіщення, замінити заголовок або додати підзаголовок. Наприклад, застосунок месенджера може отримати зашифроване сповіщення, розшифрувати його в розширенні та відобразити читабельний текст. Або новинний застосунок може додати категорію новини в підзаголовок перед показом.
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 — базовий клас, від якого успадковується Service Extension. Клас визначає два методи життєвого циклу: didReceive(_:withContentHandler:) — основний метод обробки, та serviceExtensionTimeWillExpire() — обробник вичерпання часу.
didReceive(_:withContentHandler:) викликається при отриманні сповіщення. Розширення отримує UNNotificationRequest та замикання contentHandler, яке потрібно викликати з модифікованим UNMutableNotificationContent. Розширення зобов'язане викликати contentHandler — якщо цього не зробити, iOS покаже оригінальне сповіщення після завершення таймауту.
Важливо: розширення може обробляти лише одне сповіщення за раз. Якщо надходять кілька сповіщень одночасно, iOS створює окремі екземпляри розширення для кожного. Не можна використовувати глобальний стан для послідовної обробки.
serviceExtensionTimeWillExpire() викликається системою, коли час виконання, що залишився, добігає кінця. У цьому методі потрібно негайно викликати contentHandler з тим вмістом, який готовий на даний момент — навіть якщо медіафайл ще не завантажився. Якщо не викликати contentHandler у цьому методі, iOS покаже оригінальне сповіщення.
Рекомендується зберігати в цьому методі мінімально прийнятний вміст — наприклад, сповіщення з текстом і заголовком, але без зображення, завантаження якого не завершилося вчасно.
UNNotificationAttachment — об'єкт, створюваний розширенням для прикріплення медіафайлу до сповіщення. Розширення завантажує файл із мережі, зберігає його в тимчасову директорію та створює UNNotificationAttachment із зазначенням типу вмісту.
UNNotificationAttachment створюється за допомогою ініціалізатора init(identifier:url:options:). URL має вказувати на локальний файл у тимчасовій директорії, доступній для розширення. Після створення вкладення додається до масиву attachments UNMutableNotificationContent.
Apple рекомендує використовувати URLSession з фоновою конфігурацією для завантаження — при використанні стандартної URLSession завантаження блокує потік і витрачає час із 30-секундного ліміту. Фонова URLSession продовжує завантаження навіть при завершенні розширення, а результат може бути використаний при наступному запуску.
Якщо сервер надсилає зашифроване сповіщення, розширення має розшифрувати payload перед викликом contentHandler. Розшифрування зазвичай включає запит ключа з Keychain або App Group, дешифрування через CommonCrypto та заміну body або userInfo сповіщення. При помилці розшифрування слід викликати contentHandler з оригінальним вмістом — щоб користувач хоча б бачив, що сповіщення прийшло, хай і нечитабельне.
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()
}
Notification Service Extension працює в жорстких часових рамках. iOS виділяє фіксований час на виконання — приблизно 30 секунд з моменту активації. Якщо розширення не викликало contentHandler за цей час, система примусово завершує процес і показує оригінальне сповіщення без змін.
Рекомендується реалізувати багаторівневий fallback: спочатку спробувати завантажити медіа, при успіху — викликати contentHandler з повним вмістом; при невдачі — викликати contentHandler з текстом, але без медіа; при критичній помилці — передати оригінальний вміст. Такий підхід гарантує, що користувач завжди побачить сповіщення, а не порожній екран.
За даними Apple, найпоширенішою причиною таймаутів є завантаження великих медіафайлів при повільному з'єднанні. Для зниження ризику рекомендується оптимізувати розмір зображень на сервері — надсилати прев'ю до 300 КБ замість повного розширення. Повнорозмірні зображення варто завантажувати вже при відкритті застосунку.
Для відстеження таймаутів і помилок розширення можна використовувати os_log для запису діагностичних повідомлень у Unified Logging System. Хоча пряме логування у файл у розширенні ускладнене, os_log дозволяє аналізувати продуктивність через Console.app на пристрої розробника. Apple рекомендує додавати метрики на кожен виклик didReceive — час завантаження, розмір файлу, результат операції.
override func serviceExtensionTimeWillExpire() {
let fallback = bestEffortContent as?
UNMutableNotificationContent
?? request.content.mutableCopy()
as! UNMutableNotificationContent
contentHandler(fallback)
}
Часті запитання
Сервер додає ключ mutable-content:1 у aps-словник push-сповіщення. Без цього параметра система ігнорує розширення та показує стандартне сповіщення.
Ні. mutable-content:1 — обов'язкова умова активації Service Extension. Якщо ключ відсутній або встановлений у 0, сповіщення показується без виклику розширення.
iOS примусово завершує розширення та показує оригінальне сповіщення без змін. Щоб уникнути цього, реалізуйте serviceExtensionTimeWillExpire() з мінімально прийнятним вмістом.
Через App Group (спільний UserDefaults або файл) або Keychain зі спільним доступом між застосунком і розширенням. Пряма передача ключів у payload сповіщення небезпечна.
До 4 вкладень на одне сповіщення, кожне до 50 МБ. Сумарний розмір вкладень впливає на час завантаження — чим більше файлів, тим вищий ризик таймауту.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також