Notification Service Extension — to rozszerzenie iOS, które przechwytuje powiadomienie push przed jego wyświetleniem użytkownikowi i umożliwia modyfikację treści. Rozszerzenie może odszyfrować zaszyfrowane dane, pobrać załączniki multimedialne, zmienić tekst lub dodać niestandardowe pola. Według Apple Developer, 2025, Notification Service Extension ma do 30 sekund na wykonanie zadania w tle.
Najważniejsze
Notification Service Extension — to app extension w iOS, które uruchamia się przed wyświetleniem powiadomienia push. Pozwala serwerowi wysyłać minimalny payload, a rozszerzenie — uzupełniać go treścią: pobierać obrazy, deszyfrować dane, zastępować tekst. Użytkownik widzi już gotowe, przetworzone powiadomienie.
Gdy iOS otrzymuje powiadomienie push, system sprawdza obecność rozszerzenia dla danej aplikacji. Jeśli rozszerzenie jest zarejestrowane, iOS uruchamia je w procesie tła i przekazuje treść powiadomienia przez metodę didReceive(_:withContentHandler:). Rozszerzenie przetwarza treść i wywołuje contentHandler ze zmodyfikowaną zawartością. Jeśli rozszerzenie nie zdąży w ciągu 30 sekund, system wyświetla oryginalne powiadomienie.
Service Extension jest niezbędny w scenariuszach, gdzie treść powiadomienia wymaga przetworzenia na urządzeniu. Bezpieczna komunikacja: serwer wysyła zaszyfrowany payload, rozszerzenie deszyfruje go lokalnie. Bogate multimedia: serwer wysyła URL obrazu, rozszerzenie pobiera i dołącza go. Dynamiczna lokalizacja: rozszerzenie podstawia tekst w języku urządzenia.
| Scenariusz | Bez rozszerzenia | Z rozszerzeniem |
|---|---|---|
| Obraz | Nieobsługiwany | Pobierany i wyświetlany |
| Szyfrowanie | Serwer przechowuje klucz | Deszyfracja na urządzeniu |
| Tekst | Sztywno ustawiony na serwerze | Dynamiczne podstawianie |
| Walidacja | Niesprawdzana | Anulowanie szkodliwych powiadomień |
Notification Service Extension działa w ograniczonym środowisku. Maksymalny czas wykonania — 30 sekund. Rozszerzenie nie ma dostępu do głównego magazynu aplikacji (z wyjątkiem App Group). Rozmiar rozszerzenia jest ograniczony do 50 MB. Po przekroczeniu czasu system wywołuje contentHandler z oryginalną treścią i wszystkie zmiany są tracone.
W Xcode Notification Service Extension dodaje się przez utworzenie nowego targetu typu Notification Service Extension. Xcode generuje szablon klasy NotificationService dziedziczącej po UNNotificationServiceExtension z dwiema metodami: didReceive i serviceExtensionTimeWillExpire.
W Xcode wybierz File → New → Target → Notification Service Extension. Podaj nazwę (np. PushNotificationService) i język Swift. Upewnij się, że target został dodany do głównej aplikacji i ma prawidłowy podpis. Po utworzeniu zostanie wygenerowana klasa NotificationService.swift z podstawową implementacją.
Info.plist rozszerzenia zawiera klucz NSExtension z podkluczami NSExtensionPointIdentifier (com.apple.usernotifications.service) i NSExtensionPrincipalClass (nazwa kontrolera). Opcjonalnie można określić NSExtensionAttributes z regułami aktywacji rozszerzenia. Te ustawienia Xcode generuje automatycznie.
Podstawowa implementacja nadpisuje didReceive, modyfikuje treść powiadomienia i wywołuje contentHandler. Jeśli przetwarzanie zajmuje zbyt dużo czasu, wywoływana jest metoda serviceExtensionTimeWillExpire, w której należy zakończyć pracę z bieżącym stanem.
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 = "[Przetworzono] \(bestAttemptContent.title)"
contentHandler?(bestAttemptContent)
}
}
override func serviceExtensionTimeWillExpire() {
if let contentHandler = contentHandler,
let bestAttemptContent = bestAttemptContent {
contentHandler(bestAttemptContent)
}
}
}
Jednym z kluczowych zastosowań Service Extension jest deszyfracja treści powiadomienia na urządzeniu. Serwer wysyła zaszyfrowany payload, a rozszerzenie deszyfruje go przy użyciu klucza przechowywanego w Keychain lub App Group. Gwarantuje to, że treść jest niedostępna dla przechwycenia podczas transmisji.
Serwer szyfruje treść powiadomienia przy użyciu klucza symetrycznego (AES-256). Klucz szyfrowania jest wcześniej uzgadniany między klientem a serwerem. Rozszerzenie otrzymuje zaszyfrowany ciąg w polu data payloadu, deszyfruje go i podstawia do pól powiadomienia. Klucz użytkownika może zostać pobrany z Keychain po uwierzytelnieniu.
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)
}
Klucz do deszyfracji nie powinien być przechowywany w kodzie rozszerzenia ani NSUserDefaults. Używaj iOS Keychain z dostępem przez App Group, aby zarówno aplikacja, jak i rozszerzenie mogły odczytać klucz. Do generowania klucza na kliencie używaj Security.framework z algorytmem AES-256-GCM.
Najczęstsze zastosowanie Notification Service Extension — pobieranie obrazów, GIF i wideo do późniejszego wyświetlenia w powiadomieniu. Serwer wysyła URL pliku multimedialnego, a rozszerzenie pobiera go, zapisuje w katalogu tymczasowym i tworzy UNNotificationAttachment.
Rozszerzenie otrzymuje URL obrazu z pola payloadu powiadomienia. Używając URLSession, rozszerzenie pobiera plik do katalogu tymczasowego. Po zakończeniu pobierania tworzony jest UNNotificationAttachment z lokalnym URL. Załącznik jest przekazywany do zmodyfikowanej treści. iOS automatycznie wyświetla obraz w standardowym interfejsie lub w 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 obsługuje następujące formaty do wyświetlania w powiadomieniach: JPEG, PNG, GIF (statyczne), wideo MPEG-4 do 50 MB. Dla plików audio obsługiwane są MP3, AAC i ALAC. Ważne: wszystkie pliki multimedialne muszą zostać pobrane w ciągu 30-sekundowego limitu. Dla dużych plików zaleca się użycie przycinania po stronie serwera lub progresywnego pobierania.
Zarządzanie 30-sekundowym limitem — kluczowe zadanie przy tworzeniu Notification Service Extension. Jeśli rozszerzenie nie zdąży zakończyć przetwarzania, system wywołuje serviceExtensionTimeWillExpire i wyświetla oryginalną treść. Należy przewidzieć scenariusze awaryjne dla każdego typu przetwarzania.
Podziel zadania według priorytetów. Krytyczne modyfikacje (deszyfracja, podstawowa lokalizacja) wykonuj w pierwszej kolejności. Opcjonalne ulepszenia (pobieranie obrazów, wzbogacanie tekstu) — w drugiej kolejności. Używaj URLSession z limitami czasu dla żądań sieciowych, aby nie zużywać całego limitu na jedną operację.
Jeśli pobranie obrazu się nie powiodło lub deszyfracja payloadu zwróciła błąd, rozszerzenie powinno wywołać contentHandler z oryginalną treścią. Nigdy nie kończ rozszerzenia bez wywołania contentHandler — prowadzi to do utraty powiadomienia. Bezpieczny fallback zawsze powinien być przewidziany w kodzie rozszerzenia.
override func didReceive(
request: UNNotificationRequest,
withContentHandler handler: @escaping (UNNotificationContent) -> Void
) {
let content = (request.content.mutableCopy()
as? UNMutableNotificationContent) ?? request.content
// Zadanie krytyczne: deszyfracja
var decryptedContent = tryDecryptPayload(content)
// Zadanie opcjonalne: multimedia
guard let mediaUrl = decryptedContent.userInfo["media_url"]
as? String else {
handler(decryptedContent)
return
}
downloadAndAttachMedia(
content: decryptedContent,
mediaUrl: mediaUrl,
handler: handler
)
}
Do testowania Notification Service Extension używaj Xcode: wybierz target rozszerzenia, uruchom na symulatorze i wyślij powiadomienie push przez terminal lub Firebase Console. Loguj każdy etap przetwarzania przez os_log — pomaga to diagnozować problemy z czasem i błędami ładowania.
Często zadawane pytania
System wywoła serviceExtensionTimeWillExpire, po czym wyświetli oryginalne powiadomienie bez zmian. Wszystkie pobrane pliki i modyfikacje zostaną odrzucone.
Tak, jeśli wywołasz contentHandler z pustą treścią (UNNotificationContent z pustymi polami), powiadomienie nie zostanie wyświetlone. Jest to używane do filtrowania powiadomień spamowych lub nieprawidłowych danych.
Token można przekazać w userInfo payloadu powiadomienia lub pobrać z Keychain przez App Group. Nie zaleca się przechowywania tokenów w UserDefaults rozszerzenia.
Rozszerzenie musi być podpisane tym samym certyfikatem developerskim co główna aplikacja. Dla produkcji wymagany jest certyfikat produkcyjny z włączoną capability Push Notifications.
Podłącz urządzenie do Xcode, wybierz target rozszerzenia w schemacie uruchamiania i wyślij powiadomienie push przez Firebase Console. Breakpoints w rozszerzeniu działają tak samo jak w głównej aplikacji.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również