PushKit 是 Apple 的一个框架,用于提供保证即时传递的 push 通知,主要针对 VoIP 应用程序。与标准的 APNs(Apple Push Notification service)不同,它们可能会延迟或被分组,而 PushKit 使用设备与 Apple 服务器之间的永久 TCP 连接。据 Apple Developer Documentation, 2026,PushKit 确保传递延迟低于 500 毫秒,这对于实时应用程序 — 语音和视频通话至关重要。
主要要点
PushKit 是 Apple 的框架,在 iOS 8 中引入,通过与 APNs 服务器的永久连接提供保证优先级的 push 通知传递机制。与普通通知不同,它们通过单一的 APNs 频道并可能延迟,而 PushKit 通知使用专用的更高优先级流,确保几乎实时传递。
从技术上讲,PushKit 通过设备与 Apple push 服务器之间的永久 TCP 连接工作。当服务器发送 VoIP 通知时,连接立即将其传输到设备,设备唤醒应用程序并调用 PKPushRegistry 委托人。应用程序不需要处于活动状态 — PushKit 可以从后台、终止状态甚至设备重启后唤醒它。
据 Microsoft Research(2024)关于移动平台上 push 通知延迟的研究,PushKit 通知的中位数延迟为 120–350 毫秒,而标准 APNs 通知的中位数为 1–5 秒。数量级的差异正是由专用 TCP 频道和 Apple 端的优先级处理解释的。
PushKit 支持四种类型:VoIP(用于通话)、Complication(用于 watchOS 表盘数据)、FileProvider(用于文件同步)和 PushToTalk(用于对讲机功能)。从 iOS 13 开始,只有 VoIP 类型仍然对第三方开发者广泛可用。Complication 和 FileProvider 具有窄专业应用,并且受限于 Apple 自己的生态系统。
| PushKit 类型 | 用途 | 可用性 |
|---|---|---|
| VoIP | 指示入局语音和视频通话 | iOS 8+,App Store |
| Complication | 更新 Apple Watch 表盘上的数据 | watchOS 6+ |
| FileProvider | 关于 File Provider Extension 中新文件的信号 | iOS 11+,受限 |
| PushToTalk | 企业应用程序中的对讲机功能 | iOS 16+,受限访问 |
APNs(Apple Push Notification service)— 通用的 push 通知传递服务,通过单一频道为所有应用程序工作。当频道负荷过重时,Apple 可以缓冲、分组甚至丢弃 APNs 通知。相反,PushKit 为每种通知类型使用专用连接,Apple 保证无缓冲地传递每个 VoIP-push。
差异在时间关键场景中显现:通过 APNs 传递的入局通话可能会延迟 10–30 秒,如果设备处于节能模式则可能完全不到达。PushKit 无论设备状态如何,都在 100–500 毫秒内传递相同的通知,因为它的 TCP 频道由系统按优先级保持活跃。
| 参数 | PushKit | APNs |
|---|---|---|
| 连接类型 | 永久 TCP(专用频道) | 共享频道带缓冲 |
| 中位数延迟 | 120–350 毫秒 | 1–5 秒 |
| 唤醒应用程序 | 始终,从任何状态 | 仅当应用程序未终止 |
| 载荷大小 | 最大 5 KB | 最大 4 KB |
| iOS 分组 | 否 | 是 |
PushKit 架构是围绕PKPushRegistry建立的 — 该对象将应用程序注册为接收特定类型的通知。应用程序创建 PKPushRegistry 实例,指定所需类型(例如 PKPushTypeVoIP)并分配委托人。注册后,系统自动保持与 APNs 的连接,并通过委托人传递 push 通知。
每个通知由PKPushPayload对象表示,该对象包含来自服务器的 dictionaryPayload 字典。载荷大小限制为 5 KB,足以传输通话元数据:叫叫人标识符、通话类型(音频/视频)、联系人姓名和会话 token。媒体流通过 WebRTC 或其他实时协议单独传输。
import PushKit
class PushKitManager: NSObject {
private let pushRegistry = PKPushRegistry(queue: .main)
func configure() {
pushRegistry.delegate = self
pushRegistry.desiredPushTypes = [.voIP]
}
}
PushKit 在接收到通知时调用委托方法。此时,应用程序必须从 dictionaryPayload 中提取数据并立即通过 CallKit 显示通话,否则系统可能终止后台任务。Apple 建议在 30 秒内完成处理,但对于 VoIP 通话,在第一秒之内显示通话屏幕至关重要。
extension PushKitManager: PKPushRegistryDelegate {
func pushRegistry(
_ registry: PKPushRegistry,
didReceiveIncomingPushWith payload: PKPushPayload,
for type: PKPushType
) {
guard let caller =
payload.dictionaryPayload["caller"] as? String
else { return }
CallKitManager.shared.reportIncomingCall(
uuid: UUID(),
handle: caller
)
}
}
随着 iOS 13 的发布,Apple 对 PushKit 的使用引入了严格的限制。开发者大量使用 VoIP-push 作为后台更新应用程序的隐蔽机制 — 通过 PushKit 唤醒可以在没有用户明确授权的情况下加载内容、同步数据和更新界面。Apple 认为这违反了节能概念,并将 PushKit 仅限制于指示入局通话。
现在每个 PushKit 通知必须立即导致通过 CallKit 显示入局通话。如果系统发现 PushKit 用于其他目的 — 例如用于后台同步或更新内容而不显示通话 — 应用程序可能在审核时被拒绝或与 PushKit 服务断开连接。Apple 还从 iOS 13 开始删除了通过 PushKit 更新后台数据的能力。
完整的 PushKit 集成包括注册、获取 push token 以及处理入局通知。PushKit独立请求发送通知的权限 — 不需要额外调用 UNUserNotificationCenter 来为 PushKit 本身请求权限,但可能需要用于应用程序的本地通知。注册后,系统调用 pushRegistry:didUpdatePushCredentials 方法来传递 push token,该 token 必须发送到服务器。
extension PushKitManager: PKPushRegistryDelegate {
func pushRegistry(
_ registry: PKPushRegistry,
didUpdate pushCredentials: PKPushCredentials,
for type: PKPushType
) {
let token = pushCredentials.token
.map { String(format: "%02x", $0) }
.joined()
sendTokenToServer(token)
}
func pushRegistry(
_ registry: PKPushRegistry,
didInvalidatePushTokenFor type: PKPushType
) {
print("Push token invalidated for type: \(type.rawValue)")
}
}
服务器端通过 APNs 发送 PushKit 通知,使用push-type = voip和 apns-push-type: voip 标头。与普通 APNs 不同,VoIP-push 使用自己的证书,不需要 topic 配置。载荵应包含最少量的数据来识别通话。
// VoIP push 载荷示例
{
"aps": {
"alert": {}
},
"caller": "+15551234567",
"callerName": "Alice Johnson",
"sessionId": "abc-123-def",
"hasVideo": false
}
调试 PushKit 比标准 APNs 更困难,因为PushKit在 iOS 仿真器上不工作。诊断需要物理 iPhone 或 iPad。正确工作的第一个信号是启动时调用 pushRegistry:didUpdatePushCredentials 并出现特定格式的 push token(VoIP 为 64 个十六进制字符)。如果委托人未被调用,请检查应用程序的 entitlements。
另一个常见问题是PushKit 在更新应用程序后无法传递通知。这是因为 push token 发生了变化,但服务器继续使用旧的 token。解决方案是在应用程序启动时将新的 token 发送到服务器,并在调用 pushRegistry:didInvalidatePushTokenForType 时删除无效的 token。Apple 还建议通过普通 APNs 实现备用机制。
| 问题 | 原因 | 解决方案 |
|---|---|---|
| didUpdatePushCredentials 未被调用 | 缺少 entitlements 或类型错误 | 在 Xcode 中检查 Capabilities → Push Notifications + VoIP |
| Push 延迟到来 | 设备处于低功耗模式或信号弱 | PushKit 无法绕过硬件限制 |
| 重启后无通知 | 重新安装应用后 push token 变化 | 请求新 token 并在服务器上更新 |
| App Store 因 PushKit 被拒 | PushKit 未用于通话 | 确保每次 push 都导致 reportNewIncomingCall |
常见问题
从技术上讲可以,但是毫无意义。从 iOS 13 开始,PushKit 唯一允许的应用是指示入局通话,这需要 CallKit 来显示。在不使用 CallKit 的情况下使用 PushKit 将导致应用程序在 App Store 中被拒绝。
PushKit 的最大载荷大小为5 KB(5120 字节)。这比普通 APNs 通知大 1 KB,可以传输更多关于通话的元数据。
Apple 在删除应用程序时自动清除 push token。服务器将收到注销通知,并应停止向该 token 发送 push。尝试向无效的 token 发送 push 将导致 APNs 错误 410。
PushKit 在macOS 10.14+上可用于使用 Mac Catalyst 或 AppKit 创建的 Mac 应用程序。功能与 iOS 版完全类似,包括对 VoIP 通知的支持。
使用自己的分析:跟踪从服务器发送 push 到客户端调用 didReceiveIncomingPushWithPayload 之间的时间。平均时间低于 500 毫秒表示 PushKit 工作正常。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。