推送的扩展能力 --- 打造安全的通知体验
1. 什么是 Notification Service Extension?
Notification Service Extension(通知服务扩展,简称 NSE)是 iOS 10 及以后系统引入的一种特殊的 App 扩展,它允许开发者在推送通知到达设备时对通知的内容进行修改和处理,从而实现丰富的推送通知效果。通过 NSE,App 可以在通知展示给用户之前,动态地修改通知的标题、内容,或者添加附件(图片、音频、视频等),增强用户体验。
简单来说,NSE 介于 APNs 推送服务器和系统通知显示之间,拦截并"加工"通知内容,使推送通知更加生动和个性化。
2. Notification Service Extension 的工作流程
- 推送通知到达设备 APNs 发送包含
mutable-content: 1
标记的推送通知给用户设备。 - 系统触发 Notification Service Extension 系统检测到通知 payload 中包含
mutable-content
,自动唤醒 NSE 扩展执行。 - NSE 执行处理逻辑 NSE 的入口方法
didReceive(_:withContentHandler:)
被调用,开发者在这里可以修改通知内容或下载附件等。 - 调用 Content Handler 完成通知修改 开发者处理完后调用
contentHandler
,系统将修改后的通知内容交给通知中心显示给用户。 - 超时处理 NSE 有约 30 秒的执行时间限制,超时系统会显示原始通知。
3. 使用场景举例
- 添加富媒体附件:图片、GIF、音频、视频等
- 自定义通知内容:根据业务需求,动态修改通知标题、正文
- 内容解密:对推送加密内容进行解密后再展示
- 下载远程资源:从网络拉取相关内容,提升通知表现力
- 统计或日志上报:通知展示前埋点统计
1. 修改通知标题和正文
less
override func didReceive(_ request: UNNotificationRequest,
withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
let bestContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
bestContent?.title = "【重要提示】" + (bestContent?.title ?? "")
bestContent?.body += "\n请及时查看。"
contentHandler(bestContent ?? request.content)
}
2. 动态添加本地附件资源
php
if let url = Bundle.main.url(forResource: "localImage", withExtension: "jpg") {
do {
let attachment = try UNNotificationAttachment(identifier: "image", url: url, options: nil)
bestContent?.attachments = [attachment]
} catch {
print("附件添加失败: (error)")
}
}
contentHandler(bestContent ?? request.content)
4. 如何配置 Notification Service Extension
-
新建 Target 在 Xcode 中选择
File > New > Target
,选择Notification Service Extension
模板,填写名称如MyAppNotificationService
。 -
修改 Info.plist 默认系统会生成对应的 Info.plist,确认
NSExtension
字典配置正确。一般无需手动修改。 -
配置推送 Payload 需要在 APNs 推送的 payload 中添加字段:
css{ "aps": { "alert": { "title": "标题", "body": "内容" }, "mutable-content": 1 }, "customKey": "自定义数据" }
mutable-content: 1
是触发 NSE 的关键。 -
实现处理逻辑 在 NSE 入口类
NotificationService
中重写:typescriptoverride func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { self.contentHandler = contentHandler bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent) guard let bestAttemptContent = bestAttemptContent else { contentHandler(request.content) return } // 示例:修改标题 bestAttemptContent.title = "(bestAttemptContent.title) [修改]" // 示例:下载附件(异步操作需要调用contentHandler) if let urlString = bestAttemptContent.userInfo["image-url"] as? String, let url = URL(string: urlString) { downloadAttachment(from: url) { attachment in if let attachment = attachment { bestAttemptContent.attachments = [attachment] } contentHandler(bestAttemptContent) } } else { contentHandler(bestAttemptContent) } }
-
处理超时 实现:
csharpoverride func serviceExtensionTimeWillExpire() { if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent { contentHandler(bestAttemptContent) } }
保障超时后仍能显示内容。
5. 常见问题与注意事项
- NSE 执行时间限制 通常只有约 30 秒,超时系统自动调用
serviceExtensionTimeWillExpire()
,务必保证处理逻辑高效。 - 推送 Payload 大小限制 推送负载有限制,NSE 下载远程资源可以突破内容限制,但附件大小不能太大,通常推荐几十 MB 以内。
- 用户权限 只有当用户允许推送通知且 App 已启用通知权限时,NSE 才会被调用。
- 调试技巧 可以通过 Xcode 的 Scheme 设置启动 NSE 调试,或使用真机配合 Xcode 控制台调试。
- 模拟器限制 模拟器对推送通知支持有限,尤其是扩展调试,最好在真机上调试。
6. 总结
Notification Service Extension 让开发者有能力在通知内容展示前对通知进行安全、灵活的动态处理,提升通知内容的准确性和用户体验。 它与 Notification Content Extension 搭配使用,可以共同打造出既个性化又安全的推送体系。