What am I missing in getting VOIP Push working in a test environment?

Setup and status:

  • The app is under an Enterprise dev account.
  • The app has Push Notifications, Remote Notifications and Voice over IP entitlements selected, among other, unrelated (I think) items.
  • On launch, the app successfully registers via PushKit and gets back a valid device token.
  • This token is copied from the Xcode console, and used to send VOIP pushes via the iCloud push console: https://developer.apple.com/notifications/push-notifications-console/

Results:

  • For a debug build loaded onto a physical device, the push console reports successful delivery of the VOIP push to the device. However, VOIP pushes are only received by the app when it is in the foreground. If the app is in the background or not running, it is not woken/launched.

The docs mention the need for a special VOIP push certificate, but my understanding is that is required between my server and Apple's servers. Since I am using the push console, I assume that removes the need for the certificate.

So, what am I missing?

Answered by DTS Engineer in 789610022

First off, just to be clear, when you're in the foreground are you receiving the voip pushes through PushKit? That basically validates the basic push "cycle" and the rest of my answers assumes that's working, but if you're receiving them a standard pushes then we need to sort that out first.

Anyway, if they're coming in through PushKit (in the foreground), then the next thing to rule out is that you're app hasn't been blocked by the iOS 13 CallKit Requirements:

Important

On iOS 13.0 and later, if you fail to report a call to CallKit, the system will terminate your app. Repeatedly failing to report calls may cause the system to stop delivering any more VoIP push notifications to your app. If you want to initiate a VoIP call without using CallKit, register for push notifications using the User Notifications framework instead of PushKit. For more information, see User Notifications.

Note that issues here can also make it easy to miss that your app IS in fact receiving then notification, since the system will crash your app as soon as you return from "didReceiveIncomingPushWithPayload".

Related to that point, the simplest test here is to change our Speakerbox sample to use your bundle ID, then enable it's PushKit support. That sample basically implements a full "one person" voip app, so it will actually let you test and validate that PushKit is working correctly, even though it won't actually let you "call" anyone.

This also rules out some of the simple mistakes that are often overlooked in early development. For example, I've seen a few apps that launch to a login screen and then register with PushKit after the user logs in. That works fine in the foreground, but then fails when the app is launched into the background and then user can't log in. Similar problems can happen if you're PushKit initialization is tied to a view object that doesn't actually create itself during the background launch process.

Finally, callservicesd (the daemon that actually handles PushKit/CallKit) is pretty good about logging whatever it does, so you can also look at it's log data to determine exactly what happened with the push.


-Kevin Elliott
DTS Engineer, CoreOS/Hardware

Accepted Answer

First off, just to be clear, when you're in the foreground are you receiving the voip pushes through PushKit? That basically validates the basic push "cycle" and the rest of my answers assumes that's working, but if you're receiving them a standard pushes then we need to sort that out first.

Anyway, if they're coming in through PushKit (in the foreground), then the next thing to rule out is that you're app hasn't been blocked by the iOS 13 CallKit Requirements:

Important

On iOS 13.0 and later, if you fail to report a call to CallKit, the system will terminate your app. Repeatedly failing to report calls may cause the system to stop delivering any more VoIP push notifications to your app. If you want to initiate a VoIP call without using CallKit, register for push notifications using the User Notifications framework instead of PushKit. For more information, see User Notifications.

Note that issues here can also make it easy to miss that your app IS in fact receiving then notification, since the system will crash your app as soon as you return from "didReceiveIncomingPushWithPayload".

Related to that point, the simplest test here is to change our Speakerbox sample to use your bundle ID, then enable it's PushKit support. That sample basically implements a full "one person" voip app, so it will actually let you test and validate that PushKit is working correctly, even though it won't actually let you "call" anyone.

This also rules out some of the simple mistakes that are often overlooked in early development. For example, I've seen a few apps that launch to a login screen and then register with PushKit after the user logs in. That works fine in the foreground, but then fails when the app is launched into the background and then user can't log in. Similar problems can happen if you're PushKit initialization is tied to a view object that doesn't actually create itself during the background launch process.

Finally, callservicesd (the daemon that actually handles PushKit/CallKit) is pretty good about logging whatever it does, so you can also look at it's log data to determine exactly what happened with the push.


-Kevin Elliott
DTS Engineer, CoreOS/Hardware

Thank you for the detailed reply. It turns out that the app was probably blocked from receiving the pushes. In my original testing of the VOIP pushes, I had not yet implemented the call handling via CallKit. So, I'm guessing that the app got blocked at that point. When I later added the CallKit support, I was still blocked. After reading your reply, I first tried deleting and reinstalling the app and now VOIP pushes are received in the background and the app is launched to receive them.

What am I missing in getting VOIP Push working in a test environment?
 
 
Q