Notification Service Extension to rozszerzenie iOS, które przechwytuje powiadomienie push zaraz po jego otrzymaniu, ale przed wyświetleniem użytkownikowi. Rozszerzenie może odszyfrować zaszyfrowany payload, pobrać i dołączyć plik multimedialny, zmienić tekst lub tytuł powiadomienia w czasie rzeczywistym. Według Apple Developer Documentation (2025), aby aktywować rozszerzenie, serwer musi wysłać klucz mutable-content:1 w atrybutach powiadomienia — jest to jedyny warunek uruchomienia UNNotificationServiceExtension.
Najważniejsze
Notification Service Extension to app extension w iOS, które przechwytuje przychodzące powiadomienie push po stronie urządzenia i pozwala zmienić jego zawartość, zanim użytkownik je zobaczy. Jest to jedyny typ rozszerzenia powiadomień, który działa z treścią, a nie z wyświetlaniem.
Kluczowa różnica w porównaniu z Notification Content Extension: Service Extension działa przed wyświetleniem powiadomienia i może zmienić tytuł, treść, plik dźwiękowy i załączniki. Content Extension działa po wyświetleniu i zarządza tylko wizualną prezentacją gotowego powiadomienia. Te dwa rozszerzenia mogą działać razem: Service Extension pobiera obraz, a Content Extension wyświetla go w niestandardowym interfejsie.
Rozszerzenie aktywuje się automatycznie po otrzymaniu powiadomienia push z atrybutem mutable-content:1 w słowniku aps. iOS uruchamia rozszerzenie w tle, przekazuje mu oryginalny UNNotificationRequest i oczekuje zmodyfikowanej wersji do wyświetlenia.
UNNotificationServiceExtension otrzymuje pełny UNNotificationRequest z oryginalną zawartością. Rozszerzenie może modyfikować dowolne pola UNNotificationContent: title, subtitle, body, userInfo, attachments i sound. Zmiany są stosowane przed wyświetleniem powiadomienia.
Odszyfrowanie treści — jeśli powiadomienie push zawiera zaszyfrowany payload, rozszerzenie odszyfrowuje go przed wyświetleniem. Pobieranie multimediów — dołączanie obrazu lub wideo do powiadomienia. Lokalizacja — dostosowanie tekstu powiadomienia do ustawień regionalnych urządzenia. Wzbogacanie danych — dodawanie dodatkowych informacji z lokalnego magazynu lub pamięci podręcznej.
Według Apple, najczęstszym scenariuszem w aplikacjach jest pobieranie obrazów dla bogatych powiadomień multimedialnych. Serwer wysyła URL obrazu w payload, rozszerzenie pobiera go do katalogu tymczasowego i tworzy UNNotificationAttachment, który system wyświetla w standardowym lub niestandardowym interfejsie.
Rozszerzenie może całkowicie przepisać text powiadomienia, zastąpić tytuł lub dodać subtitle. Na przykład aplikacja komunikatora może otrzymać zaszyfrowane powiadomienie, odszyfrować je w rozszerzeniu i wyświetlić czytelny tekst. Aplikacja informacyjna może dodać kategorię wiadomości w podtytule przed wyświetleniem.
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 — klasa bazowa, od której dziedziczy Service Extension. Klasa definiuje dwie metody cyklu życia: didReceive(_:withContentHandler:) — główna metoda przetwarzania, oraz serviceExtensionTimeWillExpire() — obsługa przekroczenia czasu.
didReceive(_:withContentHandler:) jest wywoływana po otrzymaniu powiadomienia. Rozszerzenie otrzymuje UNNotificationRequest i zamknięcie contentHandler, które należy wywołać ze zmodyfikowanym UNMutableNotificationContent. Rozszerzenie musi wywołać contentHandler — jeśli tego nie zrobi, iOS wyświetli oryginalne powiadomienie po przekroczeniu limitu czasu.
Ważne: rozszerzenie może przetwarzać tylko jedno powiadomienie na raz. Jeśli nadejdzie kilka powiadomień jednocześnie, iOS tworzy osobne instancje rozszerzenia dla każdego. Nie można używać stanu globalnego do sekwencyjnego przetwarzania.
serviceExtensionTimeWillExpire() jest wywoływana przez system, gdy pozostały czas wykonania dobiega końca. W tej metodzie należy natychmiast wywołać contentHandler z treścią, która jest gotowa w danym momencie — nawet jeśli plik multimedialny jeszcze się nie pobrał. Jeśli contentHandler nie zostanie wywołany w tej metodzie, iOS wyświetli oryginalne powiadomienie.
Zaleca się zachowanie w tej metodzie minimalnie akceptowalnej treści — na przykład powiadomienia z tekstem i tytułem, ale bez obrazu, którego pobranie nie zakończyło się na czas.
UNNotificationAttachment — obiekt tworzony przez rozszerzenie w celu dołączenia pliku multimedialnego do powiadomienia. Rozszerzenie pobiera plik z sieci, zapisuje go w katalogu tymczasowym i tworzy UNNotificationAttachment z określeniem typu treści.
UNNotificationAttachment jest tworzony za pomocą inicjalizatora init(identifier:url:options:). URL musi wskazywać na lokalny plik w katalogu tymczasowym dostępnym dla rozszerzenia. Po utworzeniu załącznik jest dodawany do tablicy attachments w UNMutableNotificationContent.
Apple zaleca używanie URLSession z konfiguracją w tle do pobierania — przy użyciu standardowej URLSession pobieranie blokuje wątek i zużywa czas z 30-sekundowego limitu. URLSession w tle kontynuuje pobieranie nawet po zakończeniu rozszerzenia, a wynik może być użyty przy następnym uruchomieniu.
Jeśli serwer wysyła zaszyfrowane powiadomienie, rozszerzenie musi odszyfrować payload przed wywołaniem contentHandler. Odszyfrowanie zazwyczaj obejmuje pobranie klucza z Keychain lub App Group, deszyfrowanie przez CommonCrypto i zastąpienie body lub userInfo powiadomienia. W przypadku błędu deszyfrowania należy wywołać contentHandler z oryginalną zawartością — aby użytkownik przynajmniej widział, że powiadomienie przyszło, nawet jeśli jest nieczytelne.
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 działa w ścisłych ramach czasowych. iOS przydziela stały czas na wykonanie — około 30 sekund od momentu aktywacji. Jeśli rozszerzenie nie wywoła contentHandler w tym czasie, system wymusza zakończenie procesu i wyświetla oryginalne powiadomienie bez zmian.
Zaleca się implementację wielopoziomowego fallback: najpierw spróbować pobrać multimedia, w przypadku sukcesu — wywołać contentHandler z pełną treścią; w przypadku niepowodzenia — wywołać contentHandler z tekstem, ale bez multimediów; w przypadku krytycznego błędu — przekazać oryginalną zawartość. Takie podejście gwarantuje, że użytkownik zawsze zobaczy powiadomienie, a nie pusty ekran.
Według Apple, najczęstszą przyczyną przekroczeń czasu jest pobieranie dużych plików multimedialnych przy wolnym połączeniu. Aby zmniejszyć ryzyko, zaleca się optymalizację rozmiaru obrazów na serwerze — wysyłanie podglądów do 300 KB zamiast pełnej rozdzielczości. Obrazy w pełnej rozdzielczości warto pobierać już po otwarciu aplikacji.
Do śledzenia przekroczeń czasu i błędów rozszerzenia można użyć os_log do zapisu komunikatów diagnostycznych w Unified Logging System. Mimo że bezpośrednie logowanie do pliku w rozszerzeniu jest utrudnione, os_log pozwala analizować wydajność przez Console.app na urządzeniu deweloperskim. Apple zaleca dodawanie metryk przy każdym wywołaniu didReceive — czasu pobierania, rozmiaru pliku, wyniku operacji.
override func serviceExtensionTimeWillExpire() {
let fallback = bestEffortContent as?
UNMutableNotificationContent
?? request.content.mutableCopy()
as! UNMutableNotificationContent
contentHandler(fallback)
}
Często zadawane pytania
Serwer dodaje klucz mutable-content:1 do słownika aps powiadomienia push. Bez tego parametru system ignoruje rozszerzenie i wyświetla standardowe powiadomienie.
Nie. mutable-content:1 jest obowiązkowym warunkiem aktywacji Service Extension. Jeśli klucz jest nieobecny lub ustawiony na 0, powiadomienie jest wyświetlane bez wywołania rozszerzenia.
iOS wymusza zakończenie rozszerzenia i wyświetla oryginalne powiadomienie bez zmian. Aby tego uniknąć, zaimplementuj serviceExtensionTimeWillExpire() z minimalnie akceptowalną treścią.
Przez App Group (wspólny UserDefaults lub plik) lub Keychain ze wspólnym dostępem między aplikacją a rozszerzeniem. Bezpośrednie przekazywanie kluczy w payload powiadomienia jest niebezpieczne.
Do 4 załączników na jedno powiadomienie, każdy do 50 MB. Łączny rozmiar załączników wpływa na czas pobierania — im więcej plików, tym większe ryzyko przekroczenia czasu.
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ż