Notification Service Extension 是一个 iOS 扩展,它在收到推送通知后、显示给用户之前立即拦截该通知。该扩展可以解密加密的 payload、下载并附加媒体文件、实时更改通知的文本或标题。根据 Apple Developer Documentation (2025),要激活该扩展,服务器必须在通知属性中发送 mutable-content:1 键——这是启动 UNNotificationServiceExtension 的唯一条件。
要点
Notification Service Extension 是 iOS 中的一个应用扩展,它在设备端拦截传入的推送通知,并允许在用户看到之前更改其内容。这是唯一一种处理内容而非显示的通知扩展类型。
与 Notification Content Extension 的主要区别:Service Extension 在通知显示之前工作,可以更改标题、正文、声音文件和附件。Content Extension 在显示之后工作,仅管理已完成通知的视觉呈现。这两个扩展可以协同工作:Service Extension 下载图像,Content Extension 在自定义界面中显示它。
当收到 aps 字典中包含 mutable-content:1 属性的推送通知时,扩展会自动激活。iOS 在后台启动扩展,向其传递原始 UNNotificationRequest,并等待修改后的版本进行显示。
UNNotificationServiceExtension 接收完整的 UNNotificationRequest 及其原始内容。扩展可以修改 UNNotificationContent 的任何字段:title、subtitle、body、userInfo、attachments 和 sound。更改在通知显示之前应用。
解密内容——如果推送通知包含加密的 payload,扩展在显示之前将其解密。下载媒体——将图像或视频附加到通知。本地化——根据设备的区域设置调整通知文本。数据丰富——从本地存储或缓存添加额外信息。
据 Apple 称,应用程序中最常见的场景是下载图像用于富媒体通知。服务器将图像 URL 发送到 payload 中,扩展将其下载到临时目录并创建 UNNotificationAttachment,系统在标准或自定义界面中显示该附件。
扩展可以完全重写通知的文本、替换标题或添加副标题。例如,消息应用程序可以接收加密通知,在扩展中解密并显示可读文本。或者新闻应用程序可以在显示之前将新闻类别添加到副标题中。
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——Service Extension 继承的基类。该类定义了两个生命周期方法:didReceive(_:withContentHandler:)——主要的处理方法,以及 serviceExtensionTimeWillExpire()——时间到期处理程序。
didReceive(_:withContentHandler:) 在收到通知时调用。扩展接收 UNNotificationRequest 和必须使用修改后的 UNMutableNotificationContent 调用的 contentHandler 闭包。扩展必须调用 contentHandler——如果不这样做,iOS 将在超时后显示原始通知。
重要提示:扩展一次只能处理一个通知。如果同时到达多个通知,iOS 会为每个通知创建单独的扩展实例。不能使用全局状态进行顺序处理。
serviceExtensionTimeWillExpire() 由系统在剩余执行时间即将结束时调用。在此方法中,必须立即使用当前准备好的内容调用 contentHandler——即使媒体文件尚未下载。如果在此方法中未调用 contentHandler,iOS 将显示原始通知。
建议在此方法中保留最低限度可接受的内容——例如,带有文本和标题但没有图像的通知(因为图像下载未及时完成)。
UNNotificationAttachment——由扩展创建用于将媒体文件附加到通知的对象。扩展从网络下载文件,将其保存到临时目录,并创建指定内容类型的 UNNotificationAttachment。
UNNotificationAttachment 使用初始化器 init(identifier:url:options:) 创建。URL 必须指向扩展可访问的临时目录中的本地文件。创建后,附件将添加到 UNMutableNotificationContent 的 attachments 数组中。
Apple 建议使用具有后台配置的 URLSession 进行下载——使用标准 URLSession 时,下载会阻塞线程并消耗 30 秒限制中的时间。后台 URLSession 即使在扩展结束后也会继续下载,结果可以在下次启动时使用。
如果服务器发送加密通知,扩展必须在调用 contentHandler 之前解密 payload。解密通常包括从 Keychain 或 App Group 请求密钥、通过 CommonCrypto 解密以及替换通知的 body 或 userInfo。如果解密出错,应使用原始内容调用 contentHandler——以便用户至少看到通知已到达,即使无法读取。
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 在严格的时间范围内工作。iOS 分配固定的执行时间——从激活时刻起约30秒。如果扩展在此期间未调用 contentHandler,系统将强制终止进程并显示原始通知,不做任何更改。
建议实现多级回退:首先尝试下载媒体,成功时——使用完整内容调用 contentHandler;失败时——使用文本但没有媒体调用 contentHandler;发生严重错误时——传递原始内容。这种方法确保用户始终看到通知,而不是空白屏幕。
据 Apple 称,超时最常见的原因是在慢速连接下下载大型媒体文件。为降低风险,建议优化服务器上的图像大小——发送不超过 300 KB 的预览而非完整分辨率。完整分辨率的图像应在打开应用程序时下载。
为跟踪扩展的超时和错误,可以使用 os_log 将诊断消息写入 Unified Logging System。虽然在扩展中直接记录到文件较为困难,但 os_log 允许通过开发设备上的 Console.app 分析性能。Apple 建议在每次调用 didReceive 时添加指标——下载时间、文件大小、操作结果。
override func serviceExtensionTimeWillExpire() {
let fallback = bestEffortContent as?
UNMutableNotificationContent
?? request.content.mutableCopy()
as! UNMutableNotificationContent
contentHandler(fallback)
}
常见问题
服务器将 mutable-content:1 键添加到推送通知的 aps 字典中。如果没有此参数,系统将忽略扩展并显示标准通知。
不可以。mutable-content:1 是激活 Service Extension 的强制条件。如果该键不存在或设置为 0,通知将在不调用扩展的情况下显示。
iOS 强制终止扩展并显示原始通知,不做任何更改。为避免这种情况,请使用最低限度可接受的内容实现 serviceExtensionTimeWillExpire()。
通过 App Group(共享的 UserDefaults 或文件)或具有应用程序和扩展之间共享访问权限的 Keychain。直接在通知 payload 中传递密钥是不安全的。
每个通知最多4个附件,每个附件最大 50 MB。附件的总大小影响下载时间——文件越多,超时风险越高。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。