iOS 推送通知:APNs 与 UserNotifications
iOS 推送通知:APNs 与 UserNotifications 完全指南
通过本教程,你将系统掌握 iOS 远程推送与本地通知的原理和实现。从 APNs 通信架构,到 UserNotifications 框架的实战代码,快速构建可运行的推送功能。
理解推送通知的架构
iOS 推送通知依赖苹果推送通知服务(APNs),这是一条安全、高效的远程消息传递通道。典型通信链路由三部分组成:
- 你的服务器(Provider Server):负责生成通知内容,与 APNs 建立认证连接并发送推送请求。
- APNs 服务器:苹果托管的中转服务,根据设备令牌将通知路由到目标设备。
- 用户设备:注册远程通知后获取设备令牌,接收并展示通知。
设备首次启动应用时,系统向 APNs 请求设备令牌(device token)。应用将该令牌上传到你的服务器。当需要推送时,服务器携带令牌向 APNs 发起 HTTP/2 请求,APNs 再将通知送达设备。即使应用未运行,系统也能唤醒它处理通知(需配置正确)。
使用 UserNotifications 框架
UserNotifications 是 iOS 10 引入的统一通知管理框架,同时支持远程推送和本地通知。所有通知请求都需要通过该类库进行授权、定义和处理。
请求通知授权
在应用启动时,需要向用户请求显示通知的权限。建议在 AppDelegate 的 application(_:didFinishLaunchingWithOptions:) 中完成。
import UserNotifications
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
if granted {
print("通知权限已获取")
} else if let error = error {
print("授权错误: \(error.localizedDescription)")
}
}
// 设置通知代理(必须,用于处理前台通知和操作响应)
UNUserNotificationCenter.current().delegate = self
return true
}
注册远程通知
获取权限后,调用 UIApplication 的方法注册远程通知。成功时会返回设备令牌。
func applicationDidBecomeActive(_ application: UIApplication) {
UIApplication.shared.registerForRemoteNotifications()
}
// 成功获取令牌
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let tokenString = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
// 将 tokenString 上传到你的服务器
print("Device Token: \(tokenString)")
}
// 注册失败
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("远程通知注册失败: \(error.localizedDescription)")
}
设置通知代理
为了处理前台收到的通知和用户点击操作,需要遵循 UNUserNotificationCenterDelegate 协议。通常在 AppDelegate 中实现:
extension AppDelegate: UNUserNotificationCenterDelegate {
// 前台收到通知时的处理
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
// 如果希望前台也展示横幅、声音和角标,传入对应选项
completionHandler([.banner, .sound, .badge])
}
// 用户点击通知后的处理
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
// 处理自定义数据,进行页面跳转等
completionHandler()
}
}
配置远程推送:从 APNs 认证到负载
APNs 认证方式
Provider Server 与 APNs 通信必须使用 TLS 认证,可选择两种方式:
- 基于令牌的认证(JWT):使用从 Apple Developer 账号生成的密钥(.p8 文件)签名 JWT。适合多应用场景,无需每年更新证书。
- 基于证书的认证:使用传统的 push 服务 SSL 证书(.p12)。每个应用一个证书,到期需续签。
推荐使用 JWT 认证,其优点是通用性强且管理简单。
通知负载结构
一个典型的 APNs 请求体为 JSON 格式,主要组件为 aps 字典,可包含自定义数据:
{
"aps": {
"alert": {
"title": "新消息",
"body": "您有一条新的评论"
},
"badge": 5,
"sound": "default",
"content-available": 1
},
"customKey": "自定义内容"
}
alert:通知的标题和正文。badge:应用图标上的角标数字。sound:提示音文件名(不含后缀),设为"default"使用系统音。content-available:设为1表示静默通知,用于唤醒应用执行后台刷新。
发向 APNs 的 HTTP 请求头必须包含正确的 apns-topic(应用的 Bundle ID)和认证头(authorization: bearer <jwt_token> 或证书)。
处理不同类型的通知
静默通知与后台刷新
开启 content-available: 1 的通知不会显示任何界面元素,仅唤醒应用 30 秒,允许下载内容或更新状态。App 需在 Capabilities 中开启 Background Modes 并勾选 Remote notifications。
系统会调用 AppDelegate 的特定方法处理:
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
guard let data = userInfo["customKey"] as? String else {
completionHandler(.noData)
return
}
// 执行数据下载和存储
completionHandler(.newData)
}
注意:该方法与 UserNotifications 的代理方法独立,收到静默通知时两者均可能触发。务必调用 completionHandler 通知系统任务结束。
可操作通知(Actionable Notifications)
通过注册通知分类(Category)与按钮动作(Action),可以让用户直接回复或执行操作。首先在授权后注册分类:
let replyAction = UNTextInputNotificationAction(identifier: "REPLY_ACTION", title: "回复", options: [])
let deleteAction = UNNotificationAction(identifier: "DELETE_ACTION", title: "删除", options: .destructive)
let category = UNNotificationCategory(identifier: "MESSAGE_CATEGORY", actions: [replyAction, deleteAction], intentIdentifiers: [], options: [])
UNUserNotificationCenter.current().setNotificationCategories([category])
然后在 didReceive response 代理方法中根据 response.actionIdentifier 和 categoryIdentifier 处理用户选择。
本地通知实战
UserNotifications 同样简化了本地通知的调度。本地通知无需网络,适合闹钟、提醒等场景。
创建并调度通知:
// 创建内容
let content = UNMutableNotificationContent()
content.title = "喝水提醒"
content.body = "该喝水了!"
content.sound = .default
content.badge = 1
// 设置触发条件(60秒后触发)
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 60, repeats: false)
// 生成请求,标识符用于更新或取消
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
// 添加到中心
UNUserNotificationCenter.current().add(request) { error in
if let error = error {
print("本地通知添加失败: \(error)")
}
}
可以使用的触发器包括:
UNTimeIntervalNotificationTrigger:指定秒数后触发。UNCalendarNotificationTrigger:在特定日期时间触发(如火警测试)。UNLocationNotificationTrigger:进入/离开某区域时触发(需位置权限)。
取消未触发的通知:UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers:) 或 removeAllPendingNotificationRequests()。
完整演练:从零构建推送通知
-
启用推送能力
在 Xcode 项目中,选择 Target → Signing & Capabilities,添加 Push Notifications 和 Background Modes(勾选 Remote notifications)。 -
准备认证凭证
登录 Apple Developer,进入 Keys 创建 APNs Key,下载 .p8 文件,记录 Key ID 和 Team ID。或者创建 Push SSL 证书。 -
编写应用端代码
按照上述步骤,在AppDelegate中集成授权、注册、令牌上传和代理方法。务必真机测试,模拟器不支持远程推送。 -
构建测试服务器
用 cURL 或简单脚本快速发送测试通知。以 JWT 为例,生成 JWT header 和 payload 后,用 .p8 文件签名:# 示例 cURL 命令 curl -v \ --header "authorization: bearer YOUR_JWT" \ --header "apns-topic: com.yourapp.bundleid" \ --data '{"aps":{"alert":{"title":"Hello","body":"Test push"}}}' \ --http2 \ https://api.push.apple.com/3/device/YOUR_DEVICE_TOKEN -
验证与调试
- 使用 Console.app 查看设备日志,过滤
dasd或push关键字。 - 检查 APNs 响应状态码(200 成功,400 错误请求,403 认证失败等)。
- 前台通知通过代理方法中的断点验证
userInfo内容。
- 使用 Console.app 查看设备日志,过滤
常见问题与优化建议
- 令牌失效处理:当收到 APNs 的
410状态码时表示令牌已无效,应从服务器删除。 - 通知频率控制:避免高频推送导致用户关闭权限,可合并通知或使用静默通知。
- 富媒体通知:通过添加
UNNotificationAttachment展示图片、视频,需在通知服务扩展中下载附件。 - 数据保护:推送负载中的自定义数据不会加密,敏感内容应通过静默通知唤醒应用,由应用自行从安全服务器获取。
掌握这些核心技术后,你就能为自己的 iOS 应用添加稳定、可交互的推送通知系统。本地通知则可在无网络环境中增强用户体验。持续关注 Apple 官方文档,及时适配新版本特性。