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 и приказује оригинални садржај. Потребно је предвидети fallback сценарије за сваки тип обраде.
Поделите задатке по приоритетима. Критичне измене (дешифровање, основна локализација) извршите прво. Опциона побољшања (преузимање слика, обогаћивање текста) — у другом реду. Користите URLSession са тајмаутима за мрежне захтеве, како не бисте потрошили цело ограничење на једну операцију.
Ако преузимање слике није успело или дешифровање payload-а врати грешку, екстензија треба да позове contentHandler са оригиналним садржајем. Никада не завршавајте екстензију без позивања contentHandler-а — то доводи до губитка обавештења. Сигуран fallback увек треба да буде предвиђен у коду екстензије.
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. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође