'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. 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.

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.

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. And these params are not present in the payload.



Solution 1:[1]

Since iOS 13 you can only use VoIP pushes for reporting incoming calls. 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.

because it requires params like: 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. As for example, setting the remoteHandle to CXHandle(type: .generic, value: "Connecting...") and later updating it with the correct value:

cxCallUpdate.remoteHandle = CXHandle(type: .emailAddress, value: "[email protected]")
cxProvider.reportCall(with: callUid, updated: cxCallUpdate)

Solution 2:[2]

If you are implementing incoming call as another function and calling it as below. 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. This worked for me.

Try the VOIP request with terminal command:

curl -v -d '{"callerID": "[email protected]", "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. [email protected] with related details on CXHandle.HandleType If it is your Callers Name (.generic) or email (.emailAddress) or phone number (.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.

  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.

  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).)

  5. pemfilePassword_Here is your password that you have given when you are exporting .pem file from your installed voip certificate. (refer to 4th point.)

  6. yourDeviceTokenID_generated_with_update_push_credentials_with_token_Here should be replace with the token generated by following Delegate method of PKPushRegistryDelegate. 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. (iOS 13.4.1 and Xcode Version 11.4 (11E146))

Solution 3:[3]

This is not a payload issue. You can create your own payload. Just you have to call below function in this callback.

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

Otherwise, Apple is blocking push notifications for a time.

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

Solution 4:[4]

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. I've talked with Apple developer support, they said only iOS 13 would do that but still I got error.

Solution 5:[5]

I've faced same crash when tried using async version of 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()
    }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 pepsy
Solution 2 Rakshitha Muranga Rodrigo
Solution 3 Onur K?l?รง
Solution 4 MEO
Solution 5 Pylyp Dukhov