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, който системата показва в стандартен или персонализиран интерфейс.
Разширението може напълно да презапише текста на известието, да замени заглавието или да добави subtitle. Например, приложение за съобщения може да получи криптирано известие, да го дешифрира в разширението и да покаже четим текст. Или новинарско приложение може да добави категория на новината в подзаглавието преди показване.
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 и closure 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 KB вместо пълна резолюция. Изображенията с пълна резолюция трябва да се изтеглят при отваряне на приложението.
За проследяване на таймаути и грешки на разширението може да се използва 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 MB. Общият размер на прикачените файлове влияе на времето за изтегляне — колкото повече файлове, толкова по-висок е рискът от таймаут.
Резюме
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също