如何在PushKit推送后先执行异步任务再调用reportNewIncomingCall?
问题描述
收到PushKit VoIP推送时,必须立刻调用CXProvider().reportNewIncomingCall,否则应用会被系统强制杀死,报错信息如下:
Killing app because it never posted an incoming call
to the system after receiving a PushKit VoIP push.
想先执行一个异步任务,等任务完成后再调用CXProvider().reportNewIncomingCall,请问有没有可行的实现方式?尝试用后台任务延长执行时间但未成功,代码如下:
var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) { // Start a background task to extend execution time backgroundTaskID = UIApplication.shared.beginBackgroundTask(withName: "HandleVoIPPush") { // End task if time expires UIApplication.shared.endBackgroundTask(self.backgroundTaskID) self.backgroundTaskID = .invalid } // Start your async task asyncTask { _ in provider.reportNewIncomingCall(with: callUUID, update: update) { _ in } // End the background task UIApplication.shared.endBackgroundTask(self.backgroundTaskID) self.backgroundTaskID = .invalid // Call the completion handler to let the system know you're done completion() } }
解决方案
核心限制说明
系统对PushKit VoIP推送的处理有严格时间要求:必须在收到推送后的极短时间内(通常几秒内)调用reportNewIncomingCall,后台任务无法绕过这个限制——因为系统判断的是你是否在推送回调的窗口期内上报来电,而非单纯延长后台运行时间。之前的方案失败,就是因为把上报来电的操作放在了异步任务完成后,超出了系统允许的时间窗口。
可行实现思路
正确的做法是先立刻调用reportNewIncomingCall上报来电,再在后台执行异步任务,具体步骤如下:
- 收到PushKit推送后,第一时间生成来电UUID和
CXCallUpdate(可先填充基础占位信息,比如来电号码显示"来电中..."); - 立刻调用
reportNewIncomingCall,触发系统来电UI; - 在
reportNewIncomingCall的回调或异步任务中,执行需要的后台操作; - 异步任务完成后,通过
CXProvider更新来电详细信息(比如真实号码、来电名称等)。
示例代码
func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) { // 1. 生成必要的来电基础信息 let callUUID = UUID() let update = CXCallUpdate() update.remoteHandle = CXHandle(type: .generic, value: "来电中...") update.hasVideo = false // 2. 立刻上报来电,避免被系统杀死 provider.reportNewIncomingCall(with: callUUID, update: update) { error in if let error = error { print("上报来电失败: \(error.localizedDescription)") completion() return } // 3. 上报成功后执行异步任务 self.asyncTask { result in // 4. 异步任务完成后更新来电详细信息 let updatedUpdate = CXCallUpdate() updatedUpdate.remoteHandle = CXHandle(type: .phoneNumber, value: result.realPhoneNumber) updatedUpdate.localizedCallerName = result.callerName updatedUpdate.hasVideo = result.hasVideo self.provider.reportCall(with: callUUID, updated: updatedUpdate) completion() } } // 启动后台任务确保异步任务能完成(若任务耗时较长) var backgroundTaskID = UIApplication.shared.beginBackgroundTask(withName: "VoIPAsyncTask") { UIApplication.shared.endBackgroundTask(backgroundTaskID) backgroundTaskID = .invalid } }
关键注意事项
- 绝对不能推迟
reportNewIncomingCall的调用,这是系统硬性规则,违反就会被杀死; - 上报来电时的初始信息可用占位符,后续更新后用户会看到真实信息;
- 异步任务耗时不能过长,后台任务的时间限制通常在30秒左右,超出会被系统终止;
- 确保
CXProvider已正确配置并激活(需在didFinishLaunchingWithOptions中初始化并设置代理)。
内容的提问来源于stack exchange,提问作者Ryan
相关产品推荐
相关产品推荐

