[iOS 26][SDK 26] Application never received providerDidBegin(_:) delegate

Observed few times that providerDidBegin(_:) delegate never called for the complete app session after app init(as part of this CXProvider registered) which was built with SDK 26 and running on iOS 26.

This issue observed multiple times with our testing. Since there is no providerDidBegin:, client is marking CallKit as not ready and never report any calls for VoIP APNS and ended up in app crash due to "[PKPushRegistry _terminateAppIfThereAreUnhandledVoIPPushes]"

Please refer for sysdiagnose logs : FB19778306

Observed a few times that providerDidBegin(_:) delegate never called for the complete app session after app init(as part of this CXProvider registered) which was built with SDK 26 and running on iOS 26.

Yes, it looks like there is a bug there. More specifically, calling CXProvider.init(configuration:) kicks off an XPC connection to callservicesd on a secondary thread, the end of which eventually calls providerDidBegin(_:). If that completes before you're able to call setDelegate, then you'll see the problem you're describing.

Oddly, as far as I can tell, the issue has basically been present from the beginning, so I'm not sure why I haven't heard of this before. Are you creating CXProvider on a background thread? I generally recommend using the main thread for CallKit and PushKit, and it's possible that initializing them on a background thread might make this problem more likely due to differences in thread priority.

Moving to here:

This issue was observed multiple times with our testing. Since there is no providerDidBegin:, the client is marking CallKit as not ready and never reports any calls for VoIP APNS and ended up in an app crash due to "[PKPushRegistry _terminateAppIfThereAreUnhandledVoIPPushes]"

Don't do this. As you noted, failing to report a call will crash your app, so skipping this is just going to get your app killed. The most likely reason you missed providerDidBegin(_:) is the timing issue above, but even if there is some delay, I think you'll still be better off reporting the call.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Thanks for the update.

Oddly, as far as I can tell, the issue has basically been present from the beginning, so I'm not sure why I haven't heard of this before. Are you creating CXProvider on a background thread? I generally recommend using the main thread for CallKit and PushKit, and it's possible that initializing them on a background thread might make this problem more likely due to differences in thread priority.

Here as mentioned in the feedback, the CallKit gets initialised is in Main queue, please refer the below code:

        callkitProvider = [[CXProvider alloc] initWithConfiguration:config];
        [callkitProvider setDelegate:self queue:dispatch_get_main_queue()];

But client has getting initiated in secondary queue for the PushKit,

                voipRegistry = [[PKPushRegistry alloc] initWithQueue: voipPushNotificationQueue];

does this could lead to the issue regarding not received providerDidBegin(_:) delegate ?

Here as mentioned in the feedback, the CallKit gets initialised is in Main queue, please refer the below code:

That's the queue you're targeting, but is that code also running on the main queue?

But client has getting initiated in secondary queue for the PushKit, does this could lead to the issue regarding not received providerDidBegin(_:) delegate ?

Yes, I think that's likely a significant factor. I'm not sure why you're using a background for this, but my advice has long been to use the main thread for both of these delegates. In both cases, the delegate should be doing minimal work that does not block, so neither should disrupt the main thread.

However, the other issue here also here:

Since there is no providerDidBegin:, client is marking CallKit as not ready

Expanding on what I said earlier, I think the simplest answer here is to simply ignore providerDidBegin and treat the CXProvider as fully functional from the moment it's created.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Yes as mentioned earlier, CallKit gets initialised is in Main queue (the mentioned code init is happening in main queue)

That's the queue you're targeting, but is that code also running on the main queue?

As you suggested,we have now initialised PushKit on the main queue. We are currently monitoring the issue since it is not consistently reproducible.

Since the issue is not consistent we are monitoring this issue.

I'm still not entirely convinced that initialising PushKit on a secondary queue might led the root cause of this problem.

As per Feedback analysis:

The user launched the application at

[07/31 15:04:45:858]*******{}BEGIN LOGGING{}*****

Here user has put app to background

[07/31 15:20:41:695][ 0x105147e80]<ALA_SIGNAL>: [OS-PLT] -[AppDelegate applicationDidEnterBackground:]

and then received an VoIP APNS and it got crashed,

[07/31 15:20:55:639][ 0x122c53680]<PushToTalk> [Pushnotif] [] <ALA_SIGNAL>: [OS-CCF] Enter -[PushNotificationManager pushRegistry:didReceiveIncomingPushWithPayload:forType:withCompletionHandler:]

and did not receive a callback for approximately 15 minutes of the entire app session.

so does really initialising PushKit on a secondary queue is the primary concern for this issue?

and did not receive a callback for approximately 15 minutes of the entire app session.

What callback are you referring to here? providerDidBegin(_:)?

Jumping back to what I said here:

Yes, it looks like there is a bug there. More specifically, calling CXProvider.init(configuration:) kicks off an XPC connection to callservicesd on a secondary thread, the end of which eventually calls providerDidBegin(_:). If that completes before you're able to call setDelegate, then you'll see the problem you're describing.

The problem that's actually happening here is that the system actually DID try to call "providerDidBegin()", but was unable to do so because the delegate had not been set "yet". In other words, there is an inherent race condition between:

  1. The backend logic which "CXProvider.init(configuration:)" kicks off completing (which is what triggers "providerDidBegin()").

AND

  1. Your app calling CXProvider.setDelegate() so that the system CAN call your "providerDidBegin()".

If #1 completes before #2, then providerDidBegin() will never be called. One obvious note on all this- the FIRST thing your app should do after calling CXProvider.init(configuration:) is call CXProvider.setDelegate(). Any work that occurs between those two points will greatly increase the likelihood of failure.

so does really initialising PushKit on a secondary queue is the primary concern for this issue?

To be clear, my comment about PushKit and threading was entirely speculative. I don't know what's actually causing the race to occur, but I do suspect that some amount of thread complexity is required. Putting that another way, I don't think a single threaded app that did this on the main thread could trigger hit the race condition above:

... CXProvider.init(configuration:) ... CXProvider.setDelegate(...)

That's not because it actually eliminates the race condition (it doesn't), but is because the combination of:

  • The high scheduling priority of the main thread.

  • Lack of contention from other threads in the same process.

...would make it VERY hard for #1 to finish "first".

However, ALL of this isn't really the solution. The actual solution here is:

Expanding on what I said earlier, I think the simplest answer here is to simply ignore providerDidBegin and treat the CXProvider as fully functional from the moment it's created.

Expanding on that, there isn't really any reason for your app to delay anything based on providerDidBegin. Just start using the provider and everything will work fine.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

[iOS 26][SDK 26] Application never received providerDidBegin(_:) delegate
 
 
Q