Notification Service Extension — це розширення iOS, яке перехоплює push-сповіщення до його відображення користувачу та дозволяє модифікувати вміст. Розширення може розшифрувати зашифровані дані, завантажити медіавкладення, змінити текст або додати кастомні поля. За даними Apple Developer, 2025, Notification Service Extension має до 30 секунд на виконання завдання у фоновому режимі.
Головне
Notification Service Extension — це розширення додатку в iOS, яке запускається перед відображенням push-сповіщення. Воно дозволяє серверу надсилати мінімальний payload, а розширенню — доповнювати його контентом: завантажувати зображення, розшифровувати дані, замінювати текст. Користувач бачить вже готове, оброблене сповіщення.
Коли iOS отримує push-сповіщення, система перевіряє наявність розширення для даного додатку. Якщо розширення зареєстровано, iOS запускає його у фоновому процесі та передає вміст сповіщення через метод didReceive(_:withContentHandler:). Розширення обробляє контент та викликає contentHandler з модифікованим вмістом. Якщо розширення не встигає за 30 секунд, система відображає оригінальне сповіщення.
Service Extension необхідний у сценаріях, де контент сповіщення потребує обробки на пристрої. Безпечні комунікації: сервер надсилає зашифрований payload, розширення розшифровує його локально. Багаті медіасповіщення: сервер надсилає URL зображення, розширення завантажує та прикріплює його. Динамічна локалізація: розширення підставляє текст мовою пристрою.
| Сценарій | Без розширення | З розширенням |
|---|---|---|
| Зображення | Не підтримується | Завантажується та відображається |
| Шифрування | Сервер зберігає ключ | Розшифровка на пристрої |
| Текст | Жорстко заданий на сервері | Динамічна підстановка |
| Валідація | Не перевіряється | Скасування шкідливих сповіщень |
Notification Service Extension працює в обмеженому середовищі. Максимальний час виконання — 30 секунд. Розширення не має доступу до основного сховища додатку (крім App Group). Розмір розширення обмежений 50 МБ. При перевищенні часу система викликає contentHandler з оригінальним контентом, і всі зміни втрачаються.
У Xcode Notification Service Extension додається через створення нового target типу Notification Service Extension. Xcode генерує шаблон класу NotificationService, який успадковує від UNNotificationServiceExtension, з двома методами: didReceive та serviceExtensionTimeWillExpire.
У Xcode виберіть File → New → Target → Notification Service Extension. Вкажіть ім'я (наприклад, PushNotificationService) та мову Swift. Переконайтеся, що target додано до основного додатку та має правильний підпис. Після створення буде згенеровано клас NotificationService.swift з базовою реалізацією.
Info.plist розширення містить ключ NSExtension з підключами NSExtensionPointIdentifier (com.apple.usernotifications.service) та NSExtensionPrincipalClass (ім'я вашого контролера). Додатково можна вказати NSExtensionAttributes з правилами активації розширення. Ці налаштування Xcode генерує автоматично.
Базова реалізація перевизначає didReceive, модифікує вміст сповіщення та викликає contentHandler. Якщо обробка займає занадто багато часу, викликається serviceExtensionTimeWillExpire, де необхідно завершити роботу з поточним станом.
import UserNotifications
class NotificationService: UNNotificationServiceExtension {
var contentHandler: ((UNNotificationContent) -> Void)?
var bestAttemptContent: UNMutableNotificationContent?
override func didReceive(
request: UNNotificationRequest,
withContentHandler handler: @escaping (UNNotificationContent) -> Void
) {
contentHandler = handler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
if let bestAttemptContent = bestAttemptContent {
bestAttemptContent.title = "[Оброблено] \(bestAttemptContent.title)"
contentHandler?(bestAttemptContent)
}
}
override func serviceExtensionTimeWillExpire() {
if let contentHandler = contentHandler,
let bestAttemptContent = bestAttemptContent {
contentHandler(bestAttemptContent)
}
}
}
Одним із ключових застосувань Service Extension є розшифровка вмісту сповіщення на пристрої. Сервер надсилає зашифрований payload, а розширення розшифровує його, використовуючи ключ, що зберігається в Keychain або App Group. Це гарантує, що контент недоступний для перехоплення під час передачі.
Сервер шифрує тіло сповіщення з використанням симетричного ключа (AES-256). Ключ шифрування попередньо узгоджується між клієнтом і сервером. Розширення отримує зашифрований рядок у полі data payload, розшифровує його та підставляє в поля сповіщення. Користувацький ключ може бути отриманий з Keychain після аутентифікації.
override func didReceive(
request: UNNotificationRequest,
withContentHandler handler: @escaping (UNNotificationContent) -> Void
) {
guard let content = request.content.mutableCopy()
as? UNMutableNotificationContent else {
handler(request.content)
return
}
guard let encryptedData = content.userInfo["encrypted_data"]
as? String else {
handler(content)
return
}
let decrypted = CryptoService.decrypt(encryptedData)
content.body = decrypted.body
content.title = decrypted.title
handler(content)
}
Ключ для розшифровки не повинен зберігатися в коді розширення або NSUserDefaults. Використовуйте iOS Keychain з доступом через App Group, щоб і додаток, і розширення могли читати ключ. Для генерації ключа на клієнті використовуйте Security.framework з алгоритмом AES-256-GCM.
Найчастіше застосування Notification Service Extension — завантаження зображень, GIF та відео для подальшого відображення в сповіщенні. Сервер надсилає URL медіафайлу, а розширення завантажує його, зберігає в тимчасову директорію та створює UNNotificationAttachment.
Розширення отримує URL зображення з поля payload сповіщення. Використовуючи URLSession, розширення завантажує файл у тимчасову директорію. Після завершення завантаження створюється UNNotificationAttachment з локальним URL. Вкладення передається в модифікований контент. iOS автоматично відображає зображення в стандартному інтерфейсі або в Notification Content Extension.
private func downloadAndAttachMedia(
content: UNMutableNotificationContent,
mediaUrl: String,
handler: @escaping (UNNotificationContent) -> Void
) {
guard let url = URL(string: mediaUrl) else {
handler(content)
return
}
let task = URLSession.shared.downloadTask(with: url) { localUrl, _, error in
guard let localUrl = localUrl, error == nil else {
handler(content)
return
}
let attachment = try? UNNotificationAttachment(
identifier: "media",
url: localUrl,
options: nil
)
if let attachment = attachment {
content.attachments = [attachment]
}
handler(content)
}
task.resume()
}
iOS підтримує наступні формати для відображення в сповіщеннях: JPEG, PNG, GIF (статичні), MPEG-4 відео до 50 МБ. Для аудіофайлів підтримується MP3, AAC та ALAC. Важливо: всі медіафайли повинні бути завантажені протягом 30-секундного ліміту. Для великих файлів рекомендується використовувати серверне обрізання або прогресивне завантаження.
Управління 30-секундним лімітом — ключове завдання при розробці Notification Service Extension. Якщо розширення не встигає завершити обробку, система викликає serviceExtensionTimeWillExpire та відображає оригінальний контент. Необхідно передбачити fallback-сценарії для кожного типу обробки.
Розділіть завдання за пріоритетами. Критичні модифікації (розшифровка, базова локалізація) виконуйте в першу чергу. Опціональні покращення (завантаження зображень, збагачення тексту) — в другу чергу. Використовуйте URLSession з таймаутами для мережевих запитів, щоб не витрачати весь ліміт на одну операцію.
Якщо завантаження зображення не вдалося або розшифровка payload повернула помилку, розширення повинно викликати contentHandler з оригінальним контентом. Ніколи не завершуйте розширення без виклику contentHandler — це призводить до втрати сповіщення. Безпечний fallback завжди повинен бути передбачений в коді розширення.
override func didReceive(
request: UNNotificationRequest,
withContentHandler handler: @escaping (UNNotificationContent) -> Void
) {
let content = (request.content.mutableCopy()
as? UNMutableNotificationContent) ?? request.content
// Критичне завдання: розшифровка
var decryptedContent = tryDecryptPayload(content)
// Опціональне завдання: медіа
guard let mediaUrl = decryptedContent.userInfo["media_url"]
as? String else {
handler(decryptedContent)
return
}
downloadAndAttachMedia(
content: decryptedContent,
mediaUrl: mediaUrl,
handler: handler
)
}
Для тестування Notification Service Extension використовуйте Xcode: виберіть target розширення, запустіть на симуляторі та надішліть push-сповіщення через термінал або Firebase Console. Логуйте кожен етап обробки через os_log — це допомагає діагностувати проблеми з таймінгами та помилками завантаження.
Часті запитання
Система викличе serviceExtensionTimeWillExpire, після чого відобразить оригінальне сповіщення без змін. Всі завантажені файли та модифікації будуть відкинуті.
Так, якщо викликати contentHandler з порожнім контентом (UNNotificationContent з порожніми полями), сповіщення не буде показано. Це використовується для фільтрації спам-сповіщень або некоректних даних.
Токен можна передати в userInfo payload сповіщення або отримати з Keychain через App Group. Не рекомендується зберігати токени в UserDefaults розширення.
Розширення повинно бути підписано тим же сертифікатом розробника, що й основний додаток. Для продакшену потрібен production-сертифікат з включеним capability Push Notifications.
Підключіть пристрій до Xcode, виберіть target розширення в схемі запуску та надішліть push-сповіщення через Firebase Console. Breakpoints в розширенні працюють так само, як в основному додатку.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також