简体   繁体   English

原因:“杀死应用程序,因为它在收到 PushKit VoIP 推送回调后从未向系统发布传入呼叫。”

[英]reason: 'Killing app because it never posted an incoming call to the system after receiving a PushKit VoIP push callback.'

I'm looking for solutions but I has find nothing yet.我正在寻找解决方案,但我还没有找到任何东西。 My app receives only VoIP pushes and after iOS 13 I'm not able to receive push when the app is in background mode anymore.我的应用程序仅接收 VoIP 推送,在 iOS 13 之后,当应用程序处于后台模式时,我无法再接收推送。 I looked other questions but I was not able to solve my problem with solutions proposed.我查看了其他问题,但无法通过提出的解决方案解决我的问题。 There's someone who can help me?有人可以帮助我吗?

On iOS 13.0 and later, incoming Voice over IP calls must be reported when they are received and before the didReceiceIncomingPush() method finishes execution, using the CallKit framework, or the system will terminate your app.在 iOS 13.0 及更高版本上,必须在收到 IP 呼叫时以及 didReceiceIncomingPush() 方法完成执行之前使用 CallKit 框架报告传入语音呼叫,否则系统将终止您的应用程序。

Repeatedly failing to report calls may prevent your app from receiving any more incoming call notifications.反复报告呼叫失败可能会阻止您的应用程序接收更多来电通知。

Basically, you can no longer use VoIP pushes for non VoIP messaging, and will need to use regular push notifications for those.基本上,您不能再对非 VoIP 消息使用 VoIP 推送,而需要对这些消息使用常规推送通知。

I read this announce but in my app for particular types of push VoIP I can't use function reportNewIncomingCall() because it requires params like: uuid, handle, hasVideo ecc.我阅读了此公告,但在我的特定类型的推送 VoIP 应用程序中,我无法使用 function reportNewIncomingCall() ,因为它需要以下参数:uuid、handle、hasVideo ecc。 And these params are not present in the payload.并且这些参数不存在于有效负载中。

Since iOS 13 you can only use VoIP pushes for reporting incoming calls.由于 iOS 13 您只能使用 VoIP 推送来报告来电。 For pushes that are not incoming calls, you must use other alternatives (take a look at this answer here).对于不是来电的推送,您必须使用其他替代方法(在此处查看此答案)。

Repeatedly failing to report calls may prevent your app from receiving any more incoming call notifications.多次不报告来电可能会阻止您的应用接收更多来电通知。

From my tests, it seemed to block all VoIP pushes after failing to report only 2 or 3 times, and it would stay blocked for around 24h.从我的测试来看,它似乎在只报告 2 到 3 次失败后阻止了所有 VoIP 推送,并且它会保持被阻止大约 24 小时。

because it requires params like: uuid, handle, hasVideo ecc.因为它需要如下参数:uuid、handle、hasVideo ecc。 And these params are not present in the payload并且这些参数不存在于有效载荷中

If you receive a VoIP push for a new incoming call, but stil don't have the required info you listed above, you can initialize the call with "dummy" values, and later update them.如果您收到新来电的 VoIP 推送,但仍然没有上面列出的所需信息,您可以使用“虚拟”值初始化呼叫,然后再更新它们。 As for example, setting the remoteHandle to CXHandle(type: .generic, value: "Connecting...") and later updating it with the correct value:例如,将 remoteHandle 设置为CXHandle(type: .generic, value: "Connecting...")并稍后用正确的值更新它:

cxCallUpdate.remoteHandle = CXHandle(type: .emailAddress, value: "email@mail.com")
cxProvider.reportCall(with: callUid, updated: cxCallUpdate)

This is not a payload issue.这不是有效载荷问题。 You can create your own payload.您可以创建自己的有效负载。 Just you have to call below function in this callback.只是你必须在这个回调中调用 function 下面。

func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void)

Otherwise, Apple is blocking push notifications for a time.否则,Apple 会暂时阻止推送通知。

provider.reportNewIncomingCall(with: uuid, update: update) { error in
completion() }

If you are implementing incoming call as another function and calling it as below.如果您将来电实现为另一个 function 并按如下方式调用它。 It won't work它不会工作

func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
    yourIncomingCallFunction()
}

Instead Try Following:而是尝试以下操作:

func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {

            print("didReceiveIncomingPushWith payload")

            if let callerID = payload.dictionaryPayload["callerID"] as? String,
                let hasVideo = payload.dictionaryPayload["hasVideo"] as? Bool{
                let update = CXCallUpdate()
                 update.remoteHandle = CXHandle(type: .generic, value: callerID)
                 update.hasVideo = hasVideo


                 let bgTaskID = UIApplication.shared.beginBackgroundTask(expirationHandler: nil)
                self.provider?.reportNewIncomingCall(with: UUID(), update: update, completion: { (_) in
                    completion()
                })
                UIApplication.shared.endBackgroundTask(bgTaskID)
            }
        completion()
    }

This has Implemented the call provider posting call request inside the pushRegistry:didReceiveIncomingPushWith payload with completion handler function.这已经在pushRegistry:didReceiveIncomingPushWith payload with completion handler function。 This worked for me.这对我有用。

Try the VOIP request with terminal command:使用终端命令尝试 VOIP 请求:

curl -v -d '{"callerID": "test@test.com", "uuid":"YourUDIDorEmptyField", "hasVideo": false}' --http2 --cert yourvoipcertificatepemfile_Here.pem:pemfilePassword_Here https://api.development.push.apple.com/3/device/yourDeviceTokenID_generated_with_update_push_credentials_with_token_Here

Replace:代替:

  1. test@test.com with related details on CXHandle.HandleType If it is your Callers Name ( .generic ) or email ( .emailAddress ) or phone number ( .phoneNumber ) test@test.com以及有关CXHandle.HandleType的相关详细信息 如果是您的来电者姓名 ( .generic ) 或 email ( .emailAddress ) 或电话号码 ( .phoneNumber )

  2. YourUDIDorEmptyField with your UUID or you can keep it empty as when reportNewIncomingCall(with: UUID(), update: callUpdate) has initialize UUID() in the code itself. YourUDIDorEmptyField使用您的UUID或者您可以将其保留为空,因为reportNewIncomingCall(with: UUID(), update: callUpdate)在代码本身中初始化了UUID()

  3. "hasVideo": false here false is a Boolean (true/false) obviously that handles the Audio / Video call status on the incoming calling screen and you can use it to identify the call as exactly what type it is by implementing it. "hasVideo": false这里 false 是Boolean (true/false)显然它处理来电屏幕上的Audio / Video呼叫状态,您可以使用它来识别呼叫作为它的确切类型通过实现它。

  4. yourvoipcertificatepemfile_Here is the name of your .pem file that you have exported from the voip certificate ( voipcertificate.cer not directly but after generating from developer account and installing to your machine. Then in your keychain -> mycertificates export it as .pem file with a Password ( pemfilePassword_Here ).) yourvoipcertificatepemfile_Here是您从 voip 证书导出的.pem文件的名称(不是直接导出voipcertificate.cer ,而是在从开发人员帐户生成并安装到您的机器之后。然后在您的keychain -> mycertificates导出.pem文件,并带有密码( pemfilePassword_Here )。)

  5. pemfilePassword_Here is your password that you have given when you are exporting .pem file from your installed voip certificate. pemfilePassword_Here是您从已安装的 voip 证书导出.pem文件时提供的密码。 (refer to 4th point.) (参考第 4 点。)

  6. yourDeviceTokenID_generated_with_update_push_credentials_with_token_Here should be replace with the token generated by following Delegate method of PKPushRegistryDelegate . yourDeviceTokenID_generated_with_update_push_credentials_with_token_Here应替换为通过以下PKPushRegistryDelegateDelegate方法生成的token See below.见下文。

    func pushRegistry(_ registry: PKPushRegistry,didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {

     let parts = pushCredentials.token.map { String(format: "%02.2hhx", $0) } let token = parts.joined() print("did update push credentials with token: \(token)") }

If you have any concern please let me know.如果您有任何疑虑,请告诉我。 I have tried and tested this on App in foreground, background, lockScreen, and even after app removed from recent apps.我已经在前台、后台、lockScreen 中的应用程序上尝试并测试了这一点,甚至在应用程序从最近的应用程序中删除之后也是如此。 (iOS 13.4.1 and Xcode Version 11.4 (11E146)) (iOS 13.4.1 和 Xcode 版本 11.4 (11E146))

iOS terminates app even it is not for iOS 13. Apple, itself says, it would terminate if the app is developed iOS 13 and later but still terminates app after fail to report. iOS 终止应用程序,即使它不是 iOS 13。苹果自己说,如果应用程序被开发,它将终止 iOS 13 及更高版本,但仍会在未能报告后终止应用程序。 I've talked with Apple developer support, they said only iOS 13 would do that but still I got error.我已经与 Apple 开发人员支持人员进行了交谈,他们说只有 iOS 13 会这样做,但我仍然遇到错误。

I've faced same crash when tried using async version of didReceiveIncomingPushWith .尝试使用异步版本的didReceiveIncomingPushWith时,我遇到了同样的崩溃。 The code I've used:我用过的代码:

func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType) async {
    guard type == .voIP else { return }

    let callUpdate = CXCallUpdate()
    let phoneNumber = CXHandle(type: .phoneNumber, value: "handle")
    callUpdate.remoteHandle = phoneNumber

    try? await callProvider.reportNewIncomingCall(with: UUID(), update: callUpdate)
}

It works fine when the app is in foreground, but is crashing in background.当应用程序在前台时它工作正常,但在后台崩溃。 Logs show that for some reason it's not getting called at all.日志显示由于某种原因它根本没有被调用。

At the same time completion version works fine:同时完成版本工作正常:

func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
    guard type == .voIP else { return }

    let callUpdate = CXCallUpdate()
    let phoneNumber = CXHandle(type: .phoneNumber, value: "handle")
    callUpdate.remoteHandle = phoneNumber

    callProvider.reportNewIncomingCall(with: UUID(), update: callUpdate) { error in
        completion()
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM