凌峰创科服务平台

iOS苹果服务器推送消息,为何推送失败?

什么是 APNs?

APNs 是苹果提供的云端推送服务,它的核心作用是:允许第三方应用将通知“推”到用户的 iOS 设备上,即使应用本身没有在运行。

iOS苹果服务器推送消息,为何推送失败?-图1
(图片来源网络,侵删)

没有 APNs 的话,如果应用想通知用户新消息,必须自己保持一个后台长连接,这会非常耗电和耗流量,APNs 将这个任务集中到了苹果的服务器上,极大地优化了 iOS 的后台通知机制。


APNs 的工作流程

一个完整的推送流程如下,理解这个流程是关键:

(这是一个简化的流程图,下面是文字详解)

  1. 开发者服务器 (Provider)

    iOS苹果服务器推送消息,为何推送失败?-图2
    (图片来源网络,侵删)

    这是你的应用后端服务器,当有需要通知用户的事件发生时(用户 A 给用户 B 发了一条新消息),你的服务器会准备一条推送消息。

  2. 向 APNs 发送推送请求

    • 你的服务器会使用一个安全的网络连接,将这条推送消息发送到 APNs 服务器。
    • 为了让 APNs 知道要把消息推送给哪个设备,你的服务器必须在消息中包含一个唯一的 设备令牌
  3. APNs 处理和路由

    • APNs 接收到请求后,会根据设备令牌查找对应的 iOS 设备。
    • APNs 会检查该设备是否:
      • 在线且开启了推送权限。
      • 安装了对应的应用。
      • 没有被用户禁用该应用的推送通知。
    • 如果一切正常,APNs 就会将消息通过安全的通道发送到目标设备。
  4. iOS 设备接收通知

    iOS苹果服务器推送消息,为何推送失败?-图3
    (图片来源网络,侵删)
    • iOS 设备的系统层接收到来自 APNs 的消息。
    • 系统会根据应用当前的运行状态和通知的配置,进行不同的处理:
      • 应用在前台:系统会将通知数据直接传递给正在运行的应用程序,由应用自行处理(在界面上显示一个徽章角标或更新 UI),用户不会看到系统级的通知弹窗。
      • 应用在后台或未运行:系统会显示一个系统级的通知弹窗、在应用图标上显示徽章角标,或者播放提示音,用户点击通知后,系统会唤醒你的应用。

核心组件详解

设备令牌

  • 是什么? 一个由 APNs 生成的、与特定设备和特定应用绑定的唯一标识符,它相当于你的应用在 APNs 上的“手机号码”。
  • 如何获取?
    1. 当你的 App 首次启动并请求推送权限时,iOS 系统会向 APNs 申请一个令牌。
    2. APNs 生成这个令牌,并通过 iOS 系统返回给你的 App。
    3. 你的 App 需要将这个令牌立即、安全地发送到你的开发者服务器。
    4. 你的服务器必须妥善存储这个令牌,以便后续发送推送。
  • 为什么重要? 这是推送的“地址”,没有正确的设备令牌,APNs 就不知道把消息发给谁。
  • 令牌失效: 如果用户卸载应用、重置系统、或者更新系统后,旧的令牌会失效,你的应用需要监听 didRegisterForRemoteNotificationsWithDeviceTokendidFailToRegisterForRemoteNotificationsWithError 这两个代理方法,在令牌变化时及时更新到你的服务器。

SSL 证书 / .p8 私钥

为了与 APNs 通信,你的开发者服务器必须进行身份验证,证明它是“合法”的,苹果提供了两种主流的认证方式:

p12 证书(传统方式)

  1. 创建证书:在 Apple Developer Portal 中,为你的 App ID 创建一个 "Apple Push Notification service SSL (Sandbox & Production)" 证书。
  2. 下载证书:下载这个 .cer 文件。
  3. 生成私钥:在你的 Mac 上使用 Keychain Access 生成一个私钥,并将其与 .cer 文件导出为一个 .p12 文件,这个 .p12 文件包含了你的公钥和私钥。
  4. 使用:你的服务器需要这个 .p12 文件及其密码来建立与 APNs 的 SSL 连接。

.p8 私钥(推荐方式,更简单)

  1. 创建私钥:在 Apple Developer Portal 中,生成一个 "Auth Key (ID: YOUR_KEY_ID)",下载这个 .p8 文件。注意:.p8 文件只能下载一次,请务必保管好!
  2. 所需信息
    • .p8 文件内容。
    • Key ID:在 Portal 中可以找到。
    • Team ID:你的开发者团队 ID。
    • Bundle ID:你的应用 Bundle ID。
  3. 使用:这种方式更现代,无需密码管理,只需在代码中提供 .p8 文件和 Key ID 等信息即可生成 JWT (JSON Web Token) 用于认证。

重要提示:

  • 开发环境 vs. 生产环境:APNs 分为 Sandbox (开发)Production (生产) 两个环境,你的 .p12.p8 文件也对应这两种环境,开发时用 Sandbox,上线后必须用 Production,否则推送不到真机。
  • VoIP 推送:如果做语音通话类应用,需要申请 VoIP 证书,流程类似,但使用的是 pushkit 框架。

如何在代码中实现(iOS 端)

请求推送权限

AppDelegateSceneDelegate 中,当应用启动时,向用户请求发送通知的权限。

import UIKit
import UserNotifications
// iOS 10 及以上
func requestNotificationAuthorization() {
    let center = UNUserNotificationCenter.current()
    center.requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
        if let error = error {
            print("请求权限失败: \(error.localizedDescription)")
        }
        print("用户授权结果: \(granted)")
        // 如果用户授权,可以在这里注册获取 Device Token
        if granted {
            registerForRemoteNotifications()
        }
    }
}
// 注册获取 Device Token (iOS 3 及以上)
func registerForRemoteNotifications() {
    UIApplication.shared.registerForRemoteNotifications()
}

获取并处理 Device Token

// AppDelegate.swift
// 注册成功
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    // 将 Data 类型的 deviceToken 转换为 String
    let tokenString = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
    print("获取到 Device Token: \(tokenString)")
    // 将这个 tokenString 发送到你的服务器进行存储
    // sendTokenToServer(token: tokenString)
}
// 注册失败
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
    print("获取 Device Token 失败: \(error.localizedDescription)")
}

处理收到的通知

根据应用状态,处理接收到的通知。

// 在 SceneDelegate (iOS 13+) 或 AppDelegate 中
// 当应用在前台时收到通知
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
    // 从 userInfo 中解析数据,并跳转到对应界面
    //  handleDeepLink(userInfo: userInfo)
    print("用户点击了通知, userInfo: \(userInfo)")
    completionHandler()
}

如何在代码中实现(服务器端)

服务器端的实现取决于你使用的编程语言(Java, Python, Node.js, PHP 等),这里以 Node.js 为例,使用 apn 库。

  1. 安装库

    npm install apn
  2. 编写推送代码

const apn = require('apn');
// 1. 创建 APNs 提供者实例
// 注意:请将 YOUR_AUTH_KEY.p8 文件放在项目根目录,并填入你的 Key ID 和 Team ID
const options = {
    token: {
        key: 'YOUR_AUTH_KEY.p8', // .p8 文件的路径或内容
        keyId: 'YOUR_KEY_ID',
        teamId: 'YOUR_TEAM_ID',
    },
    production: false // 设置为 true 则连接生产环境
};
const apnProvider = new apn.Provider(options);
// 2. 准备推送通知
const note = new apn.Notification({
    alert: "你好,这是一条来自服务器的推送!",
    sound: "ping.aiff",
    badge: 1,
    payload: {
        "customData": { "type": "message", "id": 123 }
    }
});
// 3. 设备令牌 (从你的数据库中获取)
const deviceToken = 'YOUR_DEVICE_TOKEN_HERE'; // 注意:这个 token 是字符串,不是 Data
// 4. 发送推送
apnProvider.send(note, deviceToken).then((result) => {
    console.log("推送结果:", result);
}).catch((error) => {
    console.error("推送失败:", error);
});
// 5. 关闭连接 (非常重要,否则会一直占用资源)
apnProvider.shutdown();

常见问题与最佳实践

  1. 推送失败,设备令牌无效

    • 原因:用户卸载了应用、更新了 iOS 系统、或者在设置里关闭了该应用的推送权限。
    • 解决:你的服务器需要实现一个反馈服务机制,APNs 有一个专门的反馈服务,它会定期推送一个无效的设备令牌列表给你的服务器,你的服务器需要定期检查这个列表,并从数据库中删除这些无效的令牌,避免无效推送。
  2. 为什么开发机能收到,真机收不到?

    • 证书问题:检查你的服务器是否使用了 Sandbox 环境的证书/私钥。
    • Bundle ID 不匹配:你的 App ID 和 Provisioning Profile 中的 Bundle ID 必须与你的应用完全一致。
    • 权限问题:确保在真机上运行时,弹出了推送权限请求,并且用户点击了“允许”。
    • 后台模式:对于后台下载、静默推送等,需要在 Xcode 的 "Signing & Capabilities" 中开启 "Background Modes" 对应的能力。
  3. 推送到达率

    • 频率限制:APNs 对推送频率有限制,不要过于频繁地推送。
    • 内容质量:避免发送垃圾信息,否则用户会直接禁用你的推送。
    • VoIP 推送:对于即时性要求高的应用(如聊天、电话),强烈建议使用 VoIP 推送,它的优先级更高,到达速度更快。
  4. 限制

    • APNs 对通知的大小和内容有严格限制(alert 字符串长度有限制)。
    • 对于大量数据,应该只发送一个标识符(如 message_id),让 App 收到通知后,再通过网络请求从你的服务器拉取完整数据。

iOS 推送是一个涉及客户端、服务器端和苹果三方服务的完整系统,核心在于:

  • 客户端:正确请求权限、获取 Device Token 并上报给服务器。
  • 服务器端:安全地认证 APNs、使用有效的 Device Token 发送格式正确的推送消息。
  • 证书管理:分清开发/生产环境,妥善保管 .p12.p8 文件。

理解了 APNs 的工作原理和流程,你就能顺利地在你的应用中集成推送功能了。

分享:
扫描分享到社交APP
上一篇
下一篇