5

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.

Community
  • 1
  • 1
DanieleLanari
  • 101
  • 1
  • 7
  • You need to get these (the required params) in the calling void push. – Aakash Nov 18 '19 at 10:01
  • I'm not sure that this is the solution, VoIP pushes with these param are not received too when the app is in background mode – DanieleLanari Nov 18 '19 at 10:32
  • 1
    Since calls must be reported and these fields are required to report the call, you will have to modify the payload for the push. – Aakash Nov 18 '19 at 10:36
  • 1
    Does the voip push indicate an incoming call? If so the. You need to report that incoming call via CallKit. If it doesn't then you must use the normal push notification system. – Paulw11 Nov 18 '19 at 10:59
  • I use both push incoming and push calls, also of the voip type, which are normal push. In both cases neither of them arrives in backgorund mode. The only way is to manage those that do not affect the call as normal pushes? – DanieleLanari Nov 19 '19 at 08:39

5 Answers5

5

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@mail.com")
cxProvider.reportCall(with: callUid, updated: cxCallUpdate)
pepsy
  • 1,437
  • 12
  • 13
  • Do you think this solutions is also for VoIP pushes that are not push call but only pushes with personalized payload? – DanieleLanari Nov 20 '19 at 20:09
  • You cannot use VoIP pushes for something other than reporting an incoming call. There is no way to do so. – pepsy Nov 20 '19 at 20:13
3

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": "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)

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

1

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() }
Onur Kilic
  • 11
  • 1
  • 1
0

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.

Enes Okullu
  • 303
  • 3
  • 6
0

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()
    }
}
Phil Dukhov
  • 67,741
  • 15
  • 184
  • 220