Notification Service Extension — یک افزونه iOS است که اعلان push را قبل از نمایش به کاربر intercept میکند و اجازه تغییر محتوا را میدهد. این افزونه میتواند دادههای رمزگذاریشده را رمزگشایی کند، پیوستهای رسانهای را دانلود کند، متن را تغییر دهد یا فیلدهای سفارشی اضافه کند. به گفته Apple Developer, 2025, Notification Service Extension تا 30 ثانیه برای انجام وظیفه در پسزمینه فرصت دارد.
اصلی
Notification Service Extension — یک app extension در iOS است که قبل از نمایش اعلان push اجرا میشود. این امکان را میدهد که سرور payload حداقلی ارسال کند و افزونه آن را با محتوا تکمیل کند: تصاویر را دانلود کند، دادهها را رمزگشایی کند، متن را جایگزین کند. کاربر اعلان آماده و پردازششده را میبیند.
وقتی iOS یک اعلان push دریافت میکند، سیستم وجود افزونه را برای آن برنامه بررسی میکند. اگر افزونه ثبت شده باشد، iOS آن را در یک فرآیند پسزمینه اجرا میکند و محتوای اعلان را از طریق متد didReceive(_:withContentHandler:) ارسال میکند. افزونه محتوا را پردازش میکند و contentHandler را با محتوای تغییر یافته فراخوانی میکند. اگر افزونه در 30 ثانیه موفق نشود، سیستم اعلان اصلی را نمایش میدهد.
Service Extension در سناریوهایی ضروری است که محتوای اعلان نیاز به پردازش روی دستگاه دارد. ارتباطات امن: سرور payload رمزگذاریشده ارسال میکند، افزونه آن را محلی رمزگشایی میکند. اعلانهای رسانهای غنی: سرور URL تصویر را ارسال میکند، افزونه آن را دانلود و ضمیمه میکند. بومیسازی پویا: افزونه متن را به زبان دستگاه تنظیم میکند.
| سناریو | بدون افزونه | با افزونه |
|---|---|---|
| تصویر | پشتیبانی نمیشود | دانلود و نمایش داده میشود |
| رمزگذاری | سرور کلید را نگه میدارد | رمزگشایی روی دستگاه |
| متن | ثابت روی سرور تنظیم شده | جایگزینی پویا |
| اعتبارسنجی | بررسی نمیشود | لغو اعلانهای مخرب |
Notification Service Extension در یک محیط محدود کار میکند. حداکثر زمان اجرا — 30 ثانیه. افزونه به حافظه اصلی برنامه (به جز App Group) دسترسی ندارد. اندازه افزونه به 50 مگابایت محدود است. در صورت تجاوز از زمان، سیستم 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 ذخیره شده رمزگشایی میکند. این تضمین میکند که محتوا در هنگام انتقال قابل intercept نیست.
سرور بدنه اعلان را با استفاده از یک کلید متقارن (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 مگابایت. برای فایلهای صوتی MP3، AAC و ALAC پشتیبانی میشوند. مهم: تمام فایلهای رسانهای باید در محدوده 30 ثانیه دانلود شوند. برای فایلهای بزرگ، برش سمت سرور یا دانلود تدریجی توصیه میشود.
مدیریت محدودیت 30 ثانیهای — وظیفه کلیدی در توسعه Notification Service Extension است. اگر افزونه پردازش را کامل نکند، سیستم serviceExtensionTimeWillExpire را فراخوانی کرده و محتوای اصلی را نمایش میدهد. باید سناریوهای بازگشتی برای هر نوع پردازش پیشبینی شود.
وظایف را بر اساس اولویت تقسیم کنید. تغییرات حیاتی (رمزگشایی، بومیسازی پایه) را اول انجام دهید. بهبودهای اختیاری (دانلود تصاویر، غنیسازی متن) — در مرحله دوم. از URLSession با زمانهای انتظار برای درخواستهای شبکه استفاده کنید تا کل محدودیت را صرف یک عملیات نکنید.
اگر دانلود تصویر ناموفق بود یا رمزگشایی payload خطا برگرداند، افزونه باید contentHandler را با محتوای اصلی فراخوانی کند. هرگز افزونه را بدون فراخوانی contentHandler پایان ندهید — این باعث از دست رفتن اعلان میشود. بازگشت امن همیشه باید در کد افزونه پیشبینی شود.
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 افزونه توصیه نمیشود.
افزونه باید با همان گواهی توسعهدهنده برنامه اصلی امضا شود. برای تولید به گواهی تولید با قابلیت Push Notifications فعال نیاز است.
دستگاه را به Xcode متصل کنید، target افزونه را در طرح راهاندازی انتخاب کنید و یک اعلان push از طریق Firebase Console ارسال کنید. Breakpoints در افزونه مانند برنامه اصلی کار میکنند.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید