已调用 CallKit reportNewIncomingCall 完成但仍然 "Killing app because it never posted an incoming call ..." 崩溃
CallKit reportNewIncomingCall completion called but still getting "Killing app because it never posted an incoming call ..." crash
这是我第一次使用 PushKit
和 CallKit
组合来构建 VoIP 功能。我注意到从 iOS 13 开始,必须 报告 PushKit VoIP 推送,否则应用程序将崩溃。
由于这条规定,我开始在 didReceiveIncomingPushWith
中实现 reportNewIncomingCall
方法,并确保它的完成被成功调用,它确实做到了,因为我在完成块中放置的断点是已激活。
然而,不久之后,该应用程序崩溃并显示“正在终止应用程序,因为它在收到 PushKit VoIP 回调后从未向系统发布传入呼叫”,这很奇怪,因为之前调用了完成块。
有人知道为什么会这样吗?
这是reportNewIncomingCall
我实现的代码:
let callUpdate = CXCallUpdate()
callUpdate.remoteHandle = CXHandle(type: .phoneNumber, value: session)
callUpdate.localizedCallerName = username
callUpdate.hasVideo = true
callUpdate.supportsDTMF = false
let uuid = {{some uuid}}
provider.reportNewIncomingCall(with: uuid, update: callUpdate, completion: { error in
if let error = error {
print("reportNewIncomingCall error: \(error.localizedDescription)")
}
DispatchQueue.main.async {
completion()
}
})
编辑
这是pushRegistry(_, didReceiveIncomingPushWith...)
代码:
func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
if type == .voIP {
guard let payloadData = payload.dictionaryPayload as? [String: Any],
let data = payloadData["data"] as? [String: Any],
let typeCall = data["type"] as? String else { completion();return }
if typeCall == "OPEN_ACTIVITY_CALL" {
guard let userName = data["userName"] as? String else { completion();return }
self.appleCallKit?.showIncomingCall(username: userName)
}
completion()
}
}
self.appleCallKit?.showIncomingCall(username: userName)
方法在前面的代码块
中执行reportNewIncomingCall
我认为问题可能出在 DispatchQueue.main.async
。这将导致您的 completion
处理程序在以后的 运行 循环中执行,并且可能对系统来说已经太迟了。
尝试删除它:
provider.reportNewIncomingCall(with: uuid, update: callUpdate, completion: { error in
if let error = error {
print("reportNewIncomingCall error: \(error.localizedDescription)")
}
completion()
})
编辑
在您提供的新代码中,我至少可以看到三个可能导致崩溃的错误。
- 如果
guard
语句之一失败,您将不会报告新的来电,因此应用程序会崩溃。你应该这样做:
guard let payloadData = payload.dictionaryPayload as? [String: Any],
let data = payloadData["data"] as? [String: Any],
let typeCall = data["type"] as? String else {
reportFakeCall(completion) // <---
return
}
举报虚假电话并立即终止。
- 鉴于
reportNewIncomingCall
是一个异步方法,不保证pushRegistry(didReceiveIncomingPushWith...)
的completion()
会在reportNewIncomingCall
完成后被调用。因此,在某些情况下,应用程序会崩溃,因为您在完成之前没有报告新的来电。你应该这样做:
if typeCall == "OPEN_ACTIVITY_CALL" {
guard let userName = data["userName"] as? String else {
reportFakeCall(completion) // <-- As in point 1
return
}
self.appleCallKit?.showIncomingCall(username: userName, completion) // <---
}
将完成处理程序传递给 showIncomingCall
并在 reportNewIncomingCall
的完成内调用它。
- 如果
typeCall
不等于 OPEN_ACTIVITY_CALL
,您不会报告新的来电并且应用程序会崩溃。
if typeCall == "OPEN_ACTIVITY_CALL" {
...
} else {
reportFakeCall(completion)
}
您可以按如下方式实现reportFakeCall
方法:
func reportFakeCall(completion: @escaping () -> Void)
{
let callUpdate = CXCallUpdate()
let vCallId = UUID()
provider.reportNewIncomingCall(
with: vCallId,
update: callUpdate,
completion: { error in
completion()
self.endCall(with: vCallId) // CXEndCallAction transaction
})
}
这是我第一次使用 PushKit
和 CallKit
组合来构建 VoIP 功能。我注意到从 iOS 13 开始,必须 报告 PushKit VoIP 推送,否则应用程序将崩溃。
由于这条规定,我开始在 didReceiveIncomingPushWith
中实现 reportNewIncomingCall
方法,并确保它的完成被成功调用,它确实做到了,因为我在完成块中放置的断点是已激活。
然而,不久之后,该应用程序崩溃并显示“正在终止应用程序,因为它在收到 PushKit VoIP 回调后从未向系统发布传入呼叫”,这很奇怪,因为之前调用了完成块。
有人知道为什么会这样吗?
这是reportNewIncomingCall
我实现的代码:
let callUpdate = CXCallUpdate()
callUpdate.remoteHandle = CXHandle(type: .phoneNumber, value: session)
callUpdate.localizedCallerName = username
callUpdate.hasVideo = true
callUpdate.supportsDTMF = false
let uuid = {{some uuid}}
provider.reportNewIncomingCall(with: uuid, update: callUpdate, completion: { error in
if let error = error {
print("reportNewIncomingCall error: \(error.localizedDescription)")
}
DispatchQueue.main.async {
completion()
}
})
编辑
这是pushRegistry(_, didReceiveIncomingPushWith...)
代码:
func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
if type == .voIP {
guard let payloadData = payload.dictionaryPayload as? [String: Any],
let data = payloadData["data"] as? [String: Any],
let typeCall = data["type"] as? String else { completion();return }
if typeCall == "OPEN_ACTIVITY_CALL" {
guard let userName = data["userName"] as? String else { completion();return }
self.appleCallKit?.showIncomingCall(username: userName)
}
completion()
}
}
self.appleCallKit?.showIncomingCall(username: userName)
方法在前面的代码块
reportNewIncomingCall
我认为问题可能出在 DispatchQueue.main.async
。这将导致您的 completion
处理程序在以后的 运行 循环中执行,并且可能对系统来说已经太迟了。
尝试删除它:
provider.reportNewIncomingCall(with: uuid, update: callUpdate, completion: { error in
if let error = error {
print("reportNewIncomingCall error: \(error.localizedDescription)")
}
completion()
})
编辑
在您提供的新代码中,我至少可以看到三个可能导致崩溃的错误。
- 如果
guard
语句之一失败,您将不会报告新的来电,因此应用程序会崩溃。你应该这样做:
guard let payloadData = payload.dictionaryPayload as? [String: Any],
let data = payloadData["data"] as? [String: Any],
let typeCall = data["type"] as? String else {
reportFakeCall(completion) // <---
return
}
举报虚假电话并立即终止。
- 鉴于
reportNewIncomingCall
是一个异步方法,不保证pushRegistry(didReceiveIncomingPushWith...)
的completion()
会在reportNewIncomingCall
完成后被调用。因此,在某些情况下,应用程序会崩溃,因为您在完成之前没有报告新的来电。你应该这样做:
if typeCall == "OPEN_ACTIVITY_CALL" {
guard let userName = data["userName"] as? String else {
reportFakeCall(completion) // <-- As in point 1
return
}
self.appleCallKit?.showIncomingCall(username: userName, completion) // <---
}
将完成处理程序传递给 showIncomingCall
并在 reportNewIncomingCall
的完成内调用它。
- 如果
typeCall
不等于OPEN_ACTIVITY_CALL
,您不会报告新的来电并且应用程序会崩溃。
if typeCall == "OPEN_ACTIVITY_CALL" {
...
} else {
reportFakeCall(completion)
}
您可以按如下方式实现reportFakeCall
方法:
func reportFakeCall(completion: @escaping () -> Void)
{
let callUpdate = CXCallUpdate()
let vCallId = UUID()
provider.reportNewIncomingCall(
with: vCallId,
update: callUpdate,
completion: { error in
completion()
self.endCall(with: vCallId) // CXEndCallAction transaction
})
}