Notification Extension је iOS механизам који омогућава модификацију садржаја и изгледа push обавештења пре њиховог приказивања кориснику. Екстензије раде на страни оперативног система у одвојеном процесу: UNNotificationServiceExtension обрађује долазни садржај, а UNNotificationContentExtension управља интерфејсом. Према Apple Developer Documentation, 2026, Service Extension добија до 30 секунди за извршавање задатака пре приказивања обавештења. Ово ограничење је критично приликом учитавања прилога или дешифровања података.
Главне ствари
Notification Extension је софтверска компонента iOS-а која проширује стандардно понашање push обавештења, додајући могућност модификације садржаја и прилагођавања интерфејса. За разлику од обичних обавештења која систем приказује у стандардном облику, екстензије омогућавају програмеру да утиче на садржај пре приказивања и креира јединствени кориснички интерфејс.
Apple пружа два типа Notification Extension за различите намене: UNNotificationServiceExtension је одговоран за обраду долазног payload-а, а UNNotificationContentExtension управља приказивањем. Service Extension се покреће пре приказивања обавештења и има ограничено време за извршење — до 30 секунди према Apple Developer Documentation. Content Extension се активира након што корисник интерагује са обавештењем и приказује прилагођени приказ.
Стандардно iOS push обавештење систем аутоматски приказује на основу поља alert, title и subtitle из JSON payload-а. Notification Extension преузима контролу: Service Extension добија сирови payload, мења га и прослеђује систему за приказивање. Content Extension замењује стандардни банер произвољним интерфејсом са било којим контролним елементима.
Када уређај прими push обавештење са кључем mutable-content: 1, систем покреће Service Extension у одвојеном процесу. Процес екстензије је изолован од главне апликације и има сопствени пешчаник са ограничењем меморије од 50 MB. Након завршетка обраде, екстензија позива completion handler, прослеђујући измењени UNNotificationContent систему за приказивање.
UNNotificationServiceExtension је основни алат за програмску измену push обавештења пре њиховог приказивања кориснику. Екстензија се аутоматски активира при пријему обавештења у чијем payload-у је постављен флаг mutable-content: 1. У року од 30 секунди екстензија може да учита медијски прилог, измени текст, дешифрује шифроване податке или обогати садржај.
Екстензија имплементира две кључне методе из протокола UNNotificationServiceExtension. Метода didReceive прима долазни захтев са сировим UNNotificationRequest-ом и омогућава његову измену путем completion handler-а са новим UNNotificationContent-ом. Метода serviceExtensionTimeWillExpire се позива од стране системa секунду пре истека временског ограничења — у њој треба завршити обраду и проследити тренутни (евентуално делимични) резултат.
Размотримо сценарио у ком сервер шаље push са URL-ом слике у прилагођеном пољу. Service Extension учитава ову слику преко мреже, креира UNNotificationAttachment и додаје га у садржај. UNNotificationAttachment прихвата локалну датотеку слике, видеа или аудиа и аутоматски је копира у пешчаник екстензије. Након креирања прилога, екстензија прослеђује ажурирани садржај систему.
class NotificationService: UNNotificationServiceExtension {
override func didReceive(_ request: UNNotificationRequest,
withContentHandler contentHandler:
@escaping (UNNotificationContent) -> Void) {
let userInfo = request.content.userInfo
guard let imageURL = URL(string: userInfo["image-url"] as! String)
else { contentHandler(request.content); return }
let attachment = try! UNNotificationAttachment(
identifier: "image", url: imageURL,
options: [UNNotificationAttachmentOptionsTypeHintKey: "jpg"])
let modifiedContent = request.content.mutableCopy()
as! UNMutableNotificationContent
modifiedContent.attachments = [attachment]
contentHandler(modifiedContent)
}
override func serviceExtensionTimeWillExpire() {
contentHandler?(bestAttemptContent ?? request.content)
}
}
При честом слању обавештења са истим прилозима, препоручује се кеширање преузетих датотека на уређају. FileManager пружа приступ директоријуму кеша екстензије који се чува између покретања. Ово смањује време обраде наредних обавештења и смањује оптерећење мреже. Према Apple-у, кеширање прилога може смањити време обраде на 2–5 секунди уместо потпуног учитавања.
UNNotificationContentExtension омогућава замену стандардног банера обавештења прилагођеним интерфејсом креираним у Interface Builder-у или SwiftUI-ју. Екстензија се активира када корисник изврши радњу са обавештењем: додир, превлачење надоле или 3D Touch. Content Extension добија садржај који је већ обрадио Service Extension и приказује га у прилагођеном приказу.
Сваки Content Extension се везује за једну или више категорија обавештења путем Info.plist-а. Категорија се одређује на серверу пољем category у APNS payload-у. Систем аутоматски бира одговарајућу екстензију на основу категорије примљеног обавештења. Интерфејс се гради путем storyboard-а коришћењем стандардних UIKit компоненти или SwiftUI View-а.
Content Extension подржава прилагођена дугмад и обраду додира дефинисаних у UNNotificationAction-у. UNNotificationAction се креира у фази регистрације категорије и може бити унос текста путем UNTextInputNotificationAction-а. Када корисник притисне дугме, екстензија добија callback didReceive са идентификатором радње и може извршити одговарајућу логику — отворити URL, послати захтев серверу или ажурирати интерфејс.
Content Extension ради у изолованом процесу са сопственом петљом извршења и ограничењем меморије од око 50 MB. Перформансе екстензије су критичне јер системски watchdog прекида процес при прекорачењу ограничења. Препоручује се избегавање тешких прорачуна, учитавања великих слика и дугих мрежних захтева унутар Content Extension-а.
UNNotificationAttachment је објекат који додаје медијску датотеку push обавештењу: слику, видео, аудио или GIF. Прилог се креира из локалног URL-а датотеке која мора бити у пешчанику екстензије. Максимална величина прилога не сме прелазити 10 MB, у супротном ће систем одбити креирање прилога.
Apple подржава ограничени скуп формата за медијске прилоге. Слике — JPEG, PNG, GIF (укључујући анимиране), TIFF. Видео — MPEG, MP4, MOV са максималним трајањем од 30 секунди. Аудио — MP3, AAC, WAV, CAF. За сваки формат се може навести тип путем UNNotificationAttachmentOptionsTypeHintKey-а, што помаже систему да правилно обради датотеку.
Пошто push payload садржи само URL, а не саму датотеку, преузимање прилога се мора обавити унутар Service Extension-а. Максимално време преузимања је ограничено на 30 секунди, стога се препоручује коришћење URLSession-а са минималним подешавањима и искључивање преузимања при слабом сигналу. Ако прилог није преузет на време, обавештење се приказује без медија — ово је подразумевано понашање система.
Размотримо потпуни пример Notification Extension-а који учитава слику, преузима је и додаје обавештењу. NotificationService наслеђује UNNotificationServiceExtension и преписује метод didReceive. У примеру је приказана обрада опционих поља, креирање прилога и позивање completion handler-а са измењеним садржајем.
import UserNotifications
class NotificationService: UNNotificationServiceExtension {
var contentHandler: ((UNNotificationContent) -> Void)?
var bestAttemptContent: UNMutableNotificationContent?
override func didReceive(
_ request: UNNotificationRequest,
withContentHandler contentHandler:
@escaping (UNNotificationContent) -> Void
) {
self.contentHandler = contentHandler
self.bestAttemptContent =
(request.content.mutableCopy()
as! UNMutableNotificationContent)
guard let attachmentURLString =
bestAttemptContent?.userInfo["attachment-url"] as? String,
let url = URL(string: attachmentURLString)
else {
contentHandler(request.content)
return
}
URLSession.shared.downloadTask(with: url) {
[weak self] localURL, _, error in
guard let localURL = localURL, error == nil
else {
contentHandler(request.content)
return
}
let attachment = try! UNNotificationAttachment(
identifier: "media", url: localURL)
self?.bestAttemptContent?.attachments = [attachment]
contentHandler(self?.bestAttemptContent
?? request.content)
}.resume()
}
override func serviceExtensionTimeWillExpire() {
if let content = bestAttemptContent {
contentHandler?(content)
}
}
}
За правилан рад екстензије потребно је регистровати категорије обавештења у Info.plist-у. Кључ NSExtensionPointIdentifier се поставља на com.apple.usernotifications.service за Service Extension или com.apple.usernotifications.content за Content Extension. Категорије се дефинишу у AppDelegate-у при покретању апликације путем UNUserNotificationCenter-а, а екстензија се активира само за обавештења са одговарајућом категоријом у payload-у.
Често постављана питања
Service Extension мења садржај пре приказивања обавештења — додаје медије, мења текст, дешифрује податке. Content Extension замењује интерфејс обавештења прилагођеним након интеракције корисника. Service Extension ради пре приказивања, Content Extension — после.
Систем издваја 30 секунди за извршење кода у didReceive-у. Ако обрада није завршена за то време, позива се serviceExtensionTimeWillExpire, где треба проследити тренутни резултат. Препоручује се уклапање у 10–15 секунди, узимајући у обзир учитавање прилога.
Да, SwiftUI је подржан у Content Extension-у од iOS 16. View се облаже у UIHostingController и додаје на storyboard. Међутим, због ограничења меморије, препоручује се коришћење SwiftUI-ја само за једноставне интерфејсе са минималним бројем елемената.
При грешци учитавања или прекорачењу временског ограничења, једноставно позовите completion handler са оригиналним садржајем без прилога. Систем ће приказати обавештење у стандардном облику без медија. Евидентирајте грешке путем OSLog-а за дијагностику, али не блокирајте приказ обавештења.
Екстензија ради у изолованом процесу са ограничењем од око 50 MB. При прекорачењу, систем убија процес путем watchdog-а. Избегавајте учитавање великих датотека, чување слика у меморији и цурење меморије при раду са URLSession-ом.
Резиме
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође