iOS 13 Xcode 11:单App集成PKPushKit与APNS的方案咨询
嘿,针对你遇到的这个PushKit和APNS共存的问题,我刚好有过类似的社交+音视频应用的处理经验,给你梳理下清晰的解决方案:
先明确崩溃的核心原因
你遇到的[PKPushRegistry _terminateAppIfThereAreUnhandledVoIPPushes]崩溃,本质是违反了Apple的PushKit规则:所有通过PushKit接收的VOIP类型推送,必须在30秒内通过CallKit展示来电界面,或者完成合法的处理流程。如果收到VOIP推送却不处理(比如用来发普通聊天通知),系统就会直接终止你的应用。
1. 单App同时实现PushKit(VOIP)和APNS(普通推送)的正确姿势
这是完全可行的,但必须严格区分两种推送的用途和处理逻辑:
- 分开注册两种推送服务:
- 普通推送(聊天、动态提醒等):用
UNUserNotificationCenter注册,请求alert、badge、sound权限,通过UNUserNotificationCenterDelegate的方法处理前台、后台、被杀状态下的通知接收和交互。 - VOIP通话推送:用
PKPushRegistry注册,指定PKPushTypeVoIP类型,实现PKPushRegistryDelegate代理。这里要注意:PushKit的权限是系统自动授予的,不需要用户手动同意,但必须确保只有通话请求才会触发这类推送。
- 普通推送(聊天、动态提醒等):用
- 严格区分推送的处理逻辑:
- 收到VOIP推送时,必须立刻初始化CallKit的
CXProvider,调用reportNewIncomingCall方法展示来电界面——哪怕是静音提醒或者延迟接听,也不能跳过这个步骤。举个简单的代码示例:func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) { guard type == .voIP else { completion() return } // 解析payload中的通话信息(比如 caller ID、通话ID等) let callerID = payload.dictionaryPayload["caller_id"] as? String ?? "Unknown" let callUUID = UUID() // 配置CallKit来电信息 let update = CXCallUpdate() update.remoteHandle = CXHandle(type: .generic, value: callerID) update.hasVideo = true // 根据你的应用是否支持视频通话设置 // 上报来电 provider.reportNewIncomingCall(with: callUUID, update: update, completion: { error in if let error = error { print("Failed to report incoming call: \(error.localizedDescription)") } completion() }) } - 普通推送走APNS的常规流程,比如在
userNotificationCenter(_:didReceive:withCompletionHandler:)中处理通知展示、点击跳转等逻辑。
- 收到VOIP推送时,必须立刻初始化CallKit的
- 配置正确的后台模式:在Xcode的Capabilities中,同时开启
Remote notifications(给APNS用)和Voice over IP(给PushKit用)。
2. 能否仅通过PushKit达成需求?
绝对不行,而且严重违反Apple的App Store审核规则。Apple明确规定:PushKit的VOIP推送只能用于实时语音/视频通话的来电通知,不能用来发送普通的聊天、动态提醒等非通话类消息。如果滥用PushKit,不仅会导致你现在遇到的崩溃问题,还会被App Store拒绝,甚至可能触发开发者账号的警告。所以必须严格分开:通话相关用PushKit+CallKit,普通通知用APNS。
3. 应用需要做的具体修改
- 清理违规的PushKit使用:把所有非通话类的推送(比如新消息、动态更新)从PushKit切换到APNS,后端也要同步调整,只给通话请求发送VOIP类型的推送。
- 完善VOIP推送的处理逻辑:
- 确保在收到VOIP推送的30秒内完成CallKit来电上报,不能有延迟或者跳过。如果是测试用的无效VOIP推送,后端最好不要发送,避免触发崩溃。
- 完善CallKit的全生命周期处理:用户接听、拒绝、挂断通话时,要正确调用CallKit的方法更新状态,避免内存泄漏或者状态不一致。
- 检查APNS的集成完整性:
- 确认
UNUserNotificationCenter的注册流程正确,权限请求时机合理(比如用户首次打开应用时请求)。 - 测试APNS在各种状态下的表现:前台收到通知是否正常展示(如果需要)、后台/被杀状态下是否能收到通知、点击通知能否正确跳转。
- 确认
- 全面测试崩溃场景:用真机测试VOIP推送的接收流程,确保不会触发
_terminateAppIfThereAreUnhandledVoIPPushes崩溃;同时测试普通推送的各种场景,确保功能正常。
4. 其他替代方案?
其实没有合法的替代方案,因为Apple的规则是明确的:
- VOIP通话必须用PushKit+CallKit,因为PushKit能在后台/被杀状态下高优先级唤醒应用,这是APNS做不到的(APNS后台唤醒有延迟,被杀状态下只能展示通知,无法直接唤醒应用处理通话逻辑)。
- 普通通知只能用APNS,没有其他符合规则的替代方式。
- 如果你想彻底避免PushKit的崩溃风险,核心就是严格遵守规则:VOIP推送只用于通话,且必须处理CallKit。
内容的提问来源于stack exchange,提问作者Sanchit Paurush
相关产品推荐
相关产品推荐

