APNS(Apple推送通知服务)是苹果公司的一项基础设施服务,用于向生态系统中的设备(iPhone, iPad, Mac, Apple Watch和Apple TV)推送通知。该服务通过设备与苹果服务器之间的永久TLS连接确保消息的可靠传输。根据Apple Developer Documentation,APNS使用HTTP/2协议与应用程序服务器进行双向通信。
要点
Apple推送通知服务(APNS)是苹果专有的服务,用于将推送通知从应用程序服务器路由到用户设备。与FCM不同,APNS不支持Android或其他平台——它完全绑定在苹果生态系统上。
该服务通过永久TLS连接工作,每台Apple设备在开机时与APNS服务器建立此连接。该连接在后台保持,用于以最小延迟投递通知。
APNS负责整个投递基础设施:加密、身份验证、优先级排序以及在设备不可用时的重新发送。开发者只需提供正确格式化的有效载荷和有效的推送令牌。
最初APNS通过端口2195–2196上的二进制协议工作。自2015年起,苹果将该服务迁移到现代HTTP/2协议,该协议支持多路复用、头部压缩和服务器推送通知。HTTP/2自2020年6月起成为强制要求。
通过APNS推送通知的投递过程包括五个阶段:设备注册、获取推送令牌、服务器发送请求、APNS路由和投递到设备。
如果设备不可用(关机或没有网络),APNS会为每个应用程序存储最后一条消息,并在连接恢复时投递。最长存储时间为4周,之后消息被删除。
苹果支持两种在发送推送通知时验证应用程序服务器的方式。每种方式在有效期、管理和易用性方面各有特点。
| 参数 | 基于令牌(p8) | 基于证书(.p12) |
|---|---|---|
| 有效期 | 无限期(密钥不过期) | 受证书有效期限制(通常为1年) |
| 轮换 | 如果密钥未被泄露,则无需轮换 | 必须每年更换 |
| 多应用 | 一个密钥适用于账户下所有应用 | 每个应用有单独的证书 |
| 环境 | 一个密钥适用于Sandbox和Production | Sandbox和Production使用不同的证书 |
基于令牌的身份验证——自2019年以来苹果推荐的方式。您在Apple Developer Console中创建一个p8密钥,将其上传到服务器,并用它签署每个APNS请求。密钥永不过期,并适用于您账户下的所有应用程序。
对于新项目,基于令牌的身份验证无疑是更优选择:一个p8密钥覆盖整个账户,无限期有效,不受环境限制。基于证书的(.p12)身份验证仍用于传统项目,但需要每年更换证书,并且Sandbox和Production需要不同的证书。在规划CI/CD时请考虑证书的到期时间。
APNS支持三种类型的推送通知,它们在设备上的行为和对请求属性的要求各不相同。类型的选择取决于用户体验场景和消息的紧急程度。
对于Background通知,必须设置content-available:1键,并将优先级设为5(节能投递)。如果应用程序未及时处理后台通知,系统可能会限制其数量。
APNS支持两个优先级值:10(立即投递)和5(节能模式)。对于alert通知,使用10——用户应立即收到。对于background通知,使用5——系统可能会延迟投递以节省电量。在background通知中使用不正确的优先级可能导致APNS拒绝该通知。
APNS接受JSON格式的有效载荷,普通通知的最大大小为4 KB,VOIP为5 KB。有效载荷包含必需的aps字典(包含显示设置)和可选的自定义字段。
{
"aps": {
"alert": {
"title": "新消息",
"body": "您有3个未读聊天"
},
"badge": 3,
"sound": "default",
"category": "message_category",
"thread-id": "chat_room_42"
},
"customData": {
"chatId": "42"
}
}
thread-id键将通知分组到iOS通知中心。category键将通知与UNNotificationCategory关联,以显示操作按钮。没有这些键,所有通知都将单独显示。
除了必需的aps字典外,APNS有效载荷可以在顶层包含任何自定义字段。这些字段在处理通知时通过userInfo字典提供给应用程序。自定义数据便于传递实体标识符、屏幕或链接。有效载荷的最大大小为4 KB,因此避免通过推送传输大量数据;请在打开通知后通过API加载数据。
要在服务器上发送推送通知,需要向APNS端点发送带有正确身份验证头部的POST请求。以下是一个使用基于令牌身份验证的Node.js示例。
const http2 = require("http2")
const fs = require("fs")
const jwt = require("jsonwebtoken")
const token = jwt.sign(
{ iss: "TEAM_ID", iat: Math.floor(Date.now() / 1000) },
fs.readFileSync("AuthKey.p8"),
{ algorithm: "ES256", keyid: "KEY_ID" }
)
const payload = JSON.stringify({
aps: { alert: { title: "你好!", body: "测试推送" } }
})
const client = http2.connect(
"https://api.push.apple.com"
)
const req = client.request({
":method": "POST",
":path": "/3/device/DEVICE_PUSH_TOKEN",
"authorization": "bearer " + token,
"apns-push-type": "alert",
"apns-topic": "com.example.app",
"apns-priority": "10"
})
req.end(payload)
req.on("response", (headers) => {
if (headers[":status"] === 200) {
console.log("推送发送成功")
}
})
发送后,APNS在成功投递时返回HTTP状态200,或在响应正文中返回带有描述的错误代码。处理token-unregistered(410)错误非常重要——应将该令牌从服务器中删除,因为应用程序已从设备上移除。
APNS为每个发送请求返回HTTP状态。成功发送返回状态200。不同的错误需要不同的处理策略。BadDeviceToken(400)或Unregistered(410)——设备令牌已过期,需要从服务器中删除。PayloadTooLarge(413)——超过4 KB限制,请减少有效载荷大小。
TooManyRequests(429)错误——超过请求限制。APNS对每秒的发送次数设置配额。收到429时,需要采用指数退避(exponential backoff)并重试发送。建议每个HTTP/2连接每秒不超过100个请求。
APNS端的错误——500和503(内部服务器错误/服务不可用)。这是苹果基础设施的临时故障。在这种情况下,请以1–5秒的延迟重试发送,最多尝试3次。在服务器完全正常工作的情况下,持续的5xx错误很少见,通常与TLS连接问题有关。
对于生产环境,务必实现所有APNS错误的日志记录,包括令牌、错误代码和时间。这将有助于快速发现证书、配额或特定设备令牌的问题。如果使用基于证书的身份验证,请定期检查证书的有效期。
常见问题
APNS通过TCP 443(HTTPS)用于HTTP/2 API。以前端口2195和2196用于二进制协议。自2020年6月起,苹果要求仅使用端口443上的HTTP/2。请确保服务器可以访问api.push.apple.com。
Sandbox——用于调试推送通知的APNS测试环境。Production——面向真实用户的生产环境。使用基于令牌的身份验证时,一个密钥适用于两种环境——端点不同:api.sandbox.push.apple.com或api.push.apple.com。
推送令牌可能在以下情况下更改:从备份恢复应用程序、重新安装应用程序、操作系统更新、网络设置重置。令牌不会更改——通过App Store进行常规应用程序更新时不会更改。服务器应将BadDeviceToken(400)错误视为删除令牌的信号。
对于普通的alert/background通知,为4 KB(4096字节)。对于通过PushKit的VOIP通知,为5 KB(5120字节)。超出大小限制会返回PayloadTooLarge(413)错误。建议保持有效载荷最小化,并通过服务器加载额外数据。
APNS无法向没有互联网连接的设备投递通知。如果设备离线,APNS会存储每个应用程序每个设备的最后一条消息,最多28天。当连接恢复时,消息将立即投递。更早的消息不会被保存。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。