Notification Service Extension: обработка на съдържанието преди показване на iOS

Автор: IT Sectr Публикувано: 2026-03-21 Време за четене: 8 мин

Notification Service Extension — е iOS разширение, което прихваща push известието преди показването му на потребителя и позволява модифициране на съдържанието. Разширението може да дешифрира криптирани данни, да изтегли медийни приложения, да промени текст или да добави персонализирани полета. Според Apple Developer, 2025, Notification Service Extension има до 30 секунди за изпълнение на задачата във фонов режим.

Основни

  • Notification Service Extension — iOS разширение за обработка на push известие преди показване.
  • Разширението наследява от UNNotificationServiceExtension и имплементира метода didReceive.
  • Достъпни до 30 секунди фоново изпълнение за изтегляне и обработка на данни.
  • Основни сценарии: дешифриране на payload, изтегляне на изображения и видео, валидация на съдържание.
  • Разширението може да забави показването на известието или напълно да го отмени чрез contentHandler.

Какво е Notification Service Extension

Notification Service Extension — е app разширение в iOS, което се стартира преди показването на push известието. Позволява на сървъра да изпраща минимален payload, а разширението да го допълва със съдържание: да изтегля изображения, да дешифрира данни, да заменя текст. Потребителят вижда готовото, обработено известие.

Как работи

Когато iOS получи push известие, системата проверява наличието на разширение за това приложение. Ако разширението е регистрирано, iOS го стартира във фонов процес и предава съдържанието на известието чрез метода didReceive(_:withContentHandler:). Разширението обработва съдържанието и извиква contentHandler с модифицираното съдържание. Ако разширението не успее в рамките на 30 секунди, системата показва оригиналното известие.

Кога да използваме Service Extension

Service Extension е необходим в сценарии, където съдържанието на известието изисква обработка на устройството. Сигурни комуникации: сървърът изпраща криптиран payload, разширението го дешифрира локално. Богати медийни известия: сървърът изпраща URL на изображение, разширението го изтегля и прикачва. Динамична локализация: разширението замества текст на езика на устройството.

СценарийБез разширениеС разширение
ИзображениеНе се поддържаИзтегля се и се показва
КриптиранеСървърът съхранява ключаДешифриране на устройството
ТекстФиксиран на сървъраДинамично заместване
ВалидацияНе се проверяваОтмяна на злонамерени известия

Ограничения и време за изпълнение

Notification Service Extension работи в ограничена среда. Максимално време за изпълнение — 30 секунди. Разширението няма достъп до основното хранилище на приложението (освен App Group). Размерът на разширението е ограничен до 50 MB. При превишаване на времето, системата извиква contentHandler с оригиналното съдържание и всички промени се губят.

Създаване на разширение и настройка на Info.plist

В Xcode, Notification Service Extension се добавя чрез създаване на нов target от тип Notification Service Extension. Xcode генерира шаблон на клас NotificationService, наследяващ UNNotificationServiceExtension, с два метода: didReceive и serviceExtensionTimeWillExpire.

Добавяне на target към проекта

В Xcode изберете File → New → Target → Notification Service Extension. Посочете име (например PushNotificationService) и език Swift. Уверете се, че target е добавен към основното приложение и има правилен подпис. След създаване ще бъде генериран клас NotificationService.swift с основна имплементация.

Структура на Info.plist на разширението

Info.plist на разширението съдържа ключ NSExtension с подключове NSExtensionPointIdentifier (com.apple.usernotifications.service) и NSExtensionPrincipalClass (името на вашия контролер). По избор може да се посочи NSExtensionAttributes с правила за активиране на разширението. Тези настройки се генерират автоматично от Xcode.

Пример за основно разширение

Основната имплементация презаписва didReceive, модифицира съдържанието на известието и извиква contentHandler. Ако обработката отнеме твърде много време, се извиква serviceExtensionTimeWillExpire, където трябва да завършите работата с текущото състояние.

swift
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)
        }
    }
}

Дешифриране на криптиран payload

Едно от ключовите приложения на Service Extension е дешифрирането на съдържанието на известието на устройството. Сървърът изпраща криптиран payload, а разширението го дешифрира, използвайки ключ, съхраняван в Keychain или App Group. Това гарантира, че съдържанието не може да бъде прихванато по време на предаване.

Архитектура на сигурна доставка

Сървърът криптира тялото на известието, използвайки симетричен ключ (AES-256). Ключът за криптиране се съгласува предварително между клиента и сървъра. Разширението получава криптиран низ в полето data на payload, дешифрира го и го поставя в полетата на известието. Потребителският ключ може да бъде получен от Keychain след удостоверяване.

swift
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.

swift
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 — това води до загуба на известието. Сигурен резервен план винаги трябва да бъде предвиден в кода на разширението.

swift
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 — това помага за диагностициране на проблеми с времето и грешки при зареждане.

Често задавани въпроси

Какво се случва, ако разширението не се вмести в 30 секунди?

Системата ще извика serviceExtensionTimeWillExpire, след което ще покаже оригиналното известие без промени. Всички изтеглени файлове и модификации ще бъдат отхвърлени.

Може ли да се отмени показването на известието от разширението?

Да, ако извикате contentHandler с празно съдържание (UNNotificationContent с празни полета), известието няма да бъде показано. Това се използва за филтриране на спам известия или некоректни данни.

Как да предам токен за оторизация за изтегляне на медия?

Токенът може да бъде предаден в userInfo payload на известието или получен от Keychain чрез App Group. Не се препоръчва съхраняване на токени в UserDefaults на разширението.

Необходима ли е специална конфигурация на сертификати за разширението?

Разширението трябва да бъде подписано със същия разработчикски сертификат като основното приложение. За продукция се изисква продукционен сертификат с активирана capability Push Notifications.

Как да дебъгвам разширението на реално устройство?

Свържете устройството към Xcode, изберете target на разширението в схемата за стартиране и изпратете push известие чрез Firebase Console. Breakpoints в разширението работят също като в основното приложение.

Обобщение

  • Notification Service Extension — iOS разширение за фонова обработка на push известия преди показване.
  • Максимално време за изпълнение — 30 секунди, след което системата показва оригиналното съдържание.
  • Основни приложения: дешифриране на payload, изтегляне на медия, валидация и локализация на съдържание.
  • Разширението наследява от UNNotificationServiceExtension с методи didReceive и serviceExtensionTimeWillExpire.
  • За изтегляне на медия използвайте URLSession, за съхранение на ключове — iOS Keychain.
  • Винаги предвиждайте резервен план — извиквайте contentHandler с оригинално съдържание при грешки.
  • Комбинирайте Service Extension с Content Extension за пълен контрол над известията.

Ще разработим мобилно приложение под ключ

IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.

Обсъдете проекта

Прочетете също