iOS 推送通知:APNs 与 UserNotifications

FreeGuideOnline 最新 2026-06-17

iOS 推送通知:APNs 与 UserNotifications 完全指南

通过本教程,你将系统掌握 iOS 远程推送与本地通知的原理和实现。从 APNs 通信架构,到 UserNotifications 框架的实战代码,快速构建可运行的推送功能。

理解推送通知的架构

iOS 推送通知依赖苹果推送通知服务(APNs),这是一条安全、高效的远程消息传递通道。典型通信链路由三部分组成:

  • 你的服务器(Provider Server):负责生成通知内容,与 APNs 建立认证连接并发送推送请求。
  • APNs 服务器:苹果托管的中转服务,根据设备令牌将通知路由到目标设备。
  • 用户设备:注册远程通知后获取设备令牌,接收并展示通知。

设备首次启动应用时,系统向 APNs 请求设备令牌(device token)。应用将该令牌上传到你的服务器。当需要推送时,服务器携带令牌向 APNs 发起 HTTP/2 请求,APNs 再将通知送达设备。即使应用未运行,系统也能唤醒它处理通知(需配置正确)。

使用 UserNotifications 框架

UserNotifications 是 iOS 10 引入的统一通知管理框架,同时支持远程推送和本地通知。所有通知请求都需要通过该类库进行授权、定义和处理。

请求通知授权

在应用启动时,需要向用户请求显示通知的权限。建议在 AppDelegateapplication(_: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.actionIdentifiercategoryIdentifier 处理用户选择。

本地通知实战

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()

完整演练:从零构建推送通知

  1. 启用推送能力
    在 Xcode 项目中,选择 Target → Signing & Capabilities,添加 Push NotificationsBackground Modes(勾选 Remote notifications)。

  2. 准备认证凭证
    登录 Apple Developer,进入 Keys 创建 APNs Key,下载 .p8 文件,记录 Key ID 和 Team ID。或者创建 Push SSL 证书。

  3. 编写应用端代码
    按照上述步骤,在 AppDelegate 中集成授权、注册、令牌上传和代理方法。务必真机测试,模拟器不支持远程推送。

  4. 构建测试服务器
    用 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
    
  5. 验证与调试

    • 使用 Console.app 查看设备日志,过滤 dasdpush 关键字。
    • 检查 APNs 响应状态码(200 成功,400 错误请求,403 认证失败等)。
    • 前台通知通过代理方法中的断点验证 userInfo 内容。

常见问题与优化建议

  • 令牌失效处理:当收到 APNs 的 410 状态码时表示令牌已无效,应从服务器删除。
  • 通知频率控制:避免高频推送导致用户关闭权限,可合并通知或使用静默通知。
  • 富媒体通知:通过添加 UNNotificationAttachment 展示图片、视频,需在通知服务扩展中下载附件。
  • 数据保护:推送负载中的自定义数据不会加密,敏感内容应通过静默通知唤醒应用,由应用自行从安全服务器获取。

掌握这些核心技术后,你就能为自己的 iOS 应用添加稳定、可交互的推送通知系统。本地通知则可在无网络环境中增强用户体验。持续关注 Apple 官方文档,及时适配新版本特性。