iOS 13 Killing app because it never posted an incoming call to the system after receiving a PushKit VoIP callback
Asked Answered
L

2

27

After upgrading to iOS beta 13 I've noticed an unpleasant thing: my app crashes sometimes on incoming VoIP pushes.

In the crash report I see the following:

iOS 13 Killing app because it never posted an incoming call to the system after receiving a PushKit VoIP callback 

Fatal Exception: NSInternalInconsistencyException
0  CoreFoundation                 0x1af21b9f0 __exceptionPreprocess
1  libobjc.A.dylib                0x1af7284fc objc_exception_throw
2  CoreFoundation                 0x1af11efec + 
 [_CFXNotificationTokenRegistration keyCallbacks]
3  Foundation                     0x1aeda1330 -[NSAssertionHandler 
 handleFailureInMethod:object:file:lineNumber:description:]
4  PushKit                        0x19caa6b54 -[PKPushRegistry 
 _terminateAppIfThereAreUnhandledVoIPPushes]
5  libdispatch.dylib              0x1afa441ec _dispatch_client_callout
6  libdispatch.dylib              0x1af9f6c6c 
_dispatch_lane_barrier_sync_invoke_and_complete
7  PushKit                        0x19caa5b74 __73-[PKPushRegistry 
 voipPayloadReceived:mustPostCall:withCompletionHandler:]_block_invoke
8  libdispatch.dylib              0x1afa43678 
 _dispatch_call_block_and_release
9  libdispatch.dylib              0x1afa441ec 
  _dispatch_client_callout

10 libdispatch.dylib              0x1af9f61f8 
_dispatch_main_queue_callback_4CF$VARIANT$mp
11 CoreFoundation                 0x1af1992a0 
CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE
12 CoreFoundation                 0x1af1942a8 __CFRunLoopRun
13 CoreFoundation                 0x1af1937ac CFRunLoopRunSpecific
14 GraphicsServices               0x1ae395180 GSEventRunModal
15 UIKitCore                      0x1b6e60244 UIApplicationMain
16 VOIPProject                    0x1009822d8 main + 25 
(AppDelegate.swift:25)
17 libdyld.dylib                  0x1af6e9e7c start

I can not understand how to fix the problem. Am I obliged to post CallKit incoming call screen whenever I receive a VoIP push? It sounds crazy because I check if the notification is valid before showing the screen with incoming call. Can anybody explain what should I do?

Lauretta answered 27/6, 2019 at 9:45 Comment(1)
According to Apple you are supposed to report the call when you receive a Voip push in iOS 13. If you don't then the OS terminates the app. So what you're seeing is what Apple say will happen if you don't report the incoming call to the OSInsanitary
P
50

On this thread from apple forums, someone from apple staff explained this:

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.

This was announced during the WWDC session "Advances in App Background Execution" https://developer.apple.com/videos/play/wwdc2019/707/


I've been searching for answers on how to adapt an app for this change, and what I could gather is the following:

Voip Pushes

When your app receive this kind of push, it will need to report a new incoming call using CallKit. Therefore, this kind of push will be exclusive for calls that use CallKit.

It's recommended that you set the notification's apns-expiration to 0, so you won't receive a push and be forced to present a call screen for a call that already expired.

Push Notifications

Regular push notifications are another option. If your server has all the information you need to write the notification text, you can send notifications that won't even run your app in the background. If you need to modify the content of the notification before presenting it to the user, you can use a Notification Service app extension, and if you need your app to be woken up and execute something in background, you can send silent push notifications.

Notification Service App Extension

To use this, you must set your notification's mutable-content to 1. This way, your extension will receive the notification before it is presented to the user, allowing you to change its content, with a 30 seconds time limit.

The cons are that your app will stay in the background, only your extension will be allowed to run. This might mean that you will need to share information and code between your app and the extension, either by using user defaults, keychain, or by sharing your entire database (which might not be a simple task if your app is not prepared for that).

Silent Push Notifications

To send silent push notifications, you must set your notification's content-available to 1 and remove it's alert, badge and sound. This notification will wake up your app in the background, and call your app delegate's didReceiveRemoteNotification.

The downsides are quite annoying for this option:

  • You will only get 30 seconds to run.
  • These notifications must have a apns-priority of 5, which might cause them to be grouped and delivered in bursts, and even throttled or not delivered.
  • If the user force closes the app, it will completely ignore all silent notifications until the user opens the app again.
Peeved answered 21/8, 2019 at 16:16 Comment(13)
As per Apple's policy, VOIP calls are not allowed in Chinese territory. So, to follow this, when user receives incoming call, i used to check if user's locale is in China. Then i never show the call to user. Otherwise i pass it to callkit. Now, this change in iOS 13 is blocking calls for user - if user enters china and leaves from there.Titanic
@MehulThakkar Yes, this change on iOS13 brings lots of complications. I’m still not happy with the solution I’ve got, but it feels like there’s no other way. In the app I’m working, users have the option not to use callkit... Then we use our own call screen and present a notification to the user for him to open the app and see the call screen. However, we used to use voip pushes for this notification, and now we’d have to use regular pushes, which will arrive with delay, probably after the callee has already given up on the call.Peeved
@MehulThakkar Building with Xcode 10 (iOS 12) may temporarily avoid this limitation in iOS13 for now. I'm in China and I can see WeChat is still using PushKit without CallKit (it can still be triggered on voice call after I force close it, and there is not splash screen when picking up). If that's true, how long will it last? if that's false, how to achieve that effect without PushKit? AFAIK remote silent push notification won't work after force closed.Fermanagh
@Fermanagh yes indeed, you can avoid that by building on Xcode 10 (iOS 12 SDK), as the code that forces the app to crash is only present on iOS 13 SDK. I've read somewhere that Apple will stop accepting updates that were built on Xcode 10 in April 2020, but I'm not sure if that's correct and I can't find the source anymore. About silent push notifications, you are correct. They won't be delivered at all if the user force closes the app.Peeved
Yes starting April 2020 Xcode 11 is required. Build with Xcode 11 section developer.apple.com/app-store/submissionsJudicious
@Fermanagh I am a little late to the discussion, but anyone could actually build using Xcode 10 and run the app on a ios13 device? I am unable to receive VoIP notifications when the app is on background/not running?Suspensor
@Suspensor yes, if you build with Xcode 10 (iOS 12 SDK), iOS 13 will not crash when receiving a VoIP push and not reporting an incoming call through CallKit. If you're doing so and not receiving the notifications, the problem should be somewhere else.Peeved
@Fermanagh Thanks for the quick answer. I've uploaded the app to testflight and installed in two devices, one with ios12.4.1 and another with ios13.1.2. The app was builded with XCode 10.3 (ios 12.4) and I can't make it work on ios 13. I do receive the notif on ios 12.Suspensor
only one person i like the answer that ios 13 is more complicated to handle the things i am working on ios 13 but when i use sinch direct code i am done to show the callkit but if i try to implent the code in my app 3 problem i am facing, 1) unable to find sinch 4.1 on cocoapods 2nd faild to fire didReceiveIncomingPushWithPayload 3) didCompleteProcessingPushPayload on this function app crashTrinia
I have a question: with 'Notification Service App Extension', #1: can I just ignore and do not show a notification? #2 - can I cancel/hide another/prev notification?Bilge
@Bilge 1: no, you cannot. best thing you can do in this scenario is muting the notification (the banner will be presented, but will produce no sound/vibrate. 2: gotta test that, will answer later if no one has answeredPeeved
@Bilge I didn't manage to find a solution for #2 and decided to post a new question for that: #59411226Peeved
My understanding is that only using VoIP push notifications is Apple's recommended way to go, while regular push notifications or silent ones are not recommended for incoming calls, but instead for other related things like IP-based text messages. See developer.apple.com/library/archive/documentation/Performance/…Parris
E
2

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()
    }
}
Execration answered 29/4, 2022 at 13:2 Comment(2)
Did you find a solution to this? I am finding the exact same when using the async version of this method, the method isn't even called when the app is backgrounded or killed.Favourable
@Favourable it's a bug and I have reported it back then, but such reports are often ignored for many years. You can simply create a Task and call completion after performing your async codeExecration

© 2022 - 2025 — McMap. All rights reserved.