Notification Service Extension — е iOS разширение, което прихваща push известието преди показването му на потребителя и позволява модифициране на съдържанието. Разширението може да дешифрира криптирани данни, да изтегли медийни приложения, да промени текст или да добави персонализирани полета. Според Apple Developer, 2025, Notification Service Extension има до 30 секунди за изпълнение на задачата във фонов режим.
Основни
Notification Service Extension — е app разширение в iOS, което се стартира преди показването на push известието. Позволява на сървъра да изпраща минимален payload, а разширението да го допълва със съдържание: да изтегля изображения, да дешифрира данни, да заменя текст. Потребителят вижда готовото, обработено известие.
Когато iOS получи push известие, системата проверява наличието на разширение за това приложение. Ако разширението е регистрирано, iOS го стартира във фонов процес и предава съдържанието на известието чрез метода didReceive(_:withContentHandler:). Разширението обработва съдържанието и извиква contentHandler с модифицираното съдържание. Ако разширението не успее в рамките на 30 секунди, системата показва оригиналното известие.
Service Extension е необходим в сценарии, където съдържанието на известието изисква обработка на устройството. Сигурни комуникации: сървърът изпраща криптиран payload, разширението го дешифрира локално. Богати медийни известия: сървърът изпраща URL на изображение, разширението го изтегля и прикачва. Динамична локализация: разширението замества текст на езика на устройството.
| Сценарий | Без разширение | С разширение |
|---|---|---|
| Изображение | Не се поддържа | Изтегля се и се показва |
| Криптиране | Сървърът съхранява ключа | Дешифриране на устройството |
| Текст | Фиксиран на сървъра | Динамично заместване |
| Валидация | Не се проверява | Отмяна на злонамерени известия |
Notification Service Extension работи в ограничена среда. Максимално време за изпълнение — 30 секунди. Разширението няма достъп до основното хранилище на приложението (освен App Group). Размерът на разширението е ограничен до 50 MB. При превишаване на времето, системата извиква 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 MB. За аудио файлове се поддържат MP3, AAC и ALAC. Важно: всички медийни файлове трябва да бъдат изтеглени в рамките на 30-секундното ограничение. За големи файлове се препоръчва сървърно изрязване или прогресивно изтегляне.
Управление на 30-секундното ограничение — ключова задача при разработката на Notification Service Extension. Ако разширението не успее да завърши обработката, системата извиква serviceExtensionTimeWillExpire и показва оригиналното съдържание. Трябва да се предвидят резервни сценарии за всеки тип обработка.
Разделете задачите по приоритети. Критичните модификации (дешифриране, основна локализация) изпълнете първи. Опционалните подобрения (изтегляне на изображения, обогатяване на текст) — на второ място. Използвайте URLSession с таймаути за мрежови заявки, за да не изразходвате цялото ограничение за една операция.
Ако изтеглянето на изображение не успее или дешифрирането на payload върне грешка, разширението трябва да извика contentHandler с оригиналното съдържание. Никога не прекратявайте разширението без да извикате contentHandler — това води до загуба на известието. Сигурен резервен план винаги трябва да бъде предвиден в кода на разширението.
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 на разширението.
Разширението трябва да бъде подписано със същия разработчикски сертификат като основното приложение. За продукция се изисква продукционен сертификат с активирана capability Push Notifications.
Свържете устройството към Xcode, изберете target на разширението в схемата за стартиране и изпратете push известие чрез Firebase Console. Breakpoints в разширението работят също като в основното приложение.
Обобщение
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също