We have an iOS app that is configured to receive VoIP notifications through the PushKit framework. When a VoIP notification arrives, we immediately report the incoming call to CallKit (as per iOS 13 guidelines) with the following code:
func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
let backgroundTaskIdentifier = UIApplication.shared.beginBackgroundTask(expirationHandler: nil)
self.callsManager.handleIncomingNotification(with: payload.dictionaryPayload, backgroundTaskIdentifier: backgroundTaskIdentifier, completion: completion)
}
CallsManager
is a singleton class that handles the incoming notification as follows:
let call = addOrUpdateCall(
service: TwilioService(),
token: voipNotification.token,
in: voipNotification.room,
with: voipNotification.nickname,
uuid: UUID())
if call.ring() != .none {
let update = CXCallUpdate()
update.remoteHandle = CXHandle(type: .emailAddress, value: voipNotification.email)
update.hasVideo = voipNotification.hasVideo
update.supportsDTMF = false
update.supportsHolding = false
update.supportsGrouping = false
update.supportsUngrouping = false
update.localizedCallerName = voipNotification.nickname
self.callProvider?.reportNewIncomingCall(with: call.uuid, update: update, completion: { (error) in
completion()
if let error = error {
self.serialQueue.async {
switch error {
case CXErrorCodeIncomingCallError.filteredByDoNotDisturb, CXErrorCodeIncomingCallError.filteredByBlockList:
_ = call.decline()
default:
call.fail(error: error)
}
}
}
})
}
Basically, we are extracting the necessary info from the notification and we're immediately displaying the incoming call screen.
The addOrUpdateCall
function just makes sure that in the array of calls that we have in memory, we are not storing duplicate calls.
Normally, this works perfectly: the CallKit incoming call screen is displayed and the phone starts ringing. But sometimes, in a very sporadic way, the CallKit screen is displayed for a second and then disappears behind the app UI.
This only happens when the app is in foreground or the phone is unlocked, it does not happen if the phone is locked.
After many many attempts, we've found a somewhat systematic way of reproducing the issue using two iOS devices:
- From one device, call the other one and hang up after a few seconds.
- Repeat this three times in a row, leaving a few seconds between each call.
- Call from the other device: on the first device, the CallKit UI is displayed for a second and then hidden behind the app, but the device keeps ringing and vibrating.
We don't have any log from iOS and the phone keeps ringing and vibrating while the app UI is in front. The only way to restore it is by entering in the multi-tasking and re-selecting the app.
I've already tried to look for a workaround, where I continuously call reportNewIncomingCall
every second, hoping that this would bring the CallKit UI in front. It seems to alleviate the issue, but it still happens every once in a while.
We've tried on multiple iPhone models and it seems that the iPhone XS and the iPhone XS Max are not affected (the CallKit UI appears and hides after a second, but it comes back automatically, even without the workaround above), but we were able to reproduce the issue on an iPhone 7, two iPhone 6S and an iPhone 6S Plus.
Can anybody help me?