So, let me start by going back to what I said here:
...is an XPC connection failure with callservicesd, typically caused by callservicesd crashing. That's not common, so if your app is causing "frequent" resets, then that's an issue that might be worth looking into.
By definition, that's not a "normal" failure. Your app is basically being notified of a failure because it's not clear what else we can/should do. That also means I don't necessarily have a great "solution", certainly not one I can guarantee to be reliable.
That leads to here:
there is no initial providerDidBegin for first CXProvider as logger misses early logs
Do you have any sense about how "long" your app is typically able to stay active, both in terms of being awake/active and in absolute terms (wake or suspended)?
This primarily comes up with long-running kiosks apps, but one of the issues that can happen to long-running apps is that as they get better and better at working for very long periods of time (in kiosk apps, this is typically days/weeks of foreground time), the more likely they are to run into strange bugs/problems that would otherwise never occur.
Eventually, this VERY much reaches a point of diminishing returns, where pushing your app lifetime further "out" in time just means whatever bug you find will be that much harder to reproduce.
CallKit apps are generally short-lived enough that this isn't really an issue, but if your app IS ending up running for long periods of time, then you may want to consider putting code in place to periodically exit as an easy way to minimize these issues.
if our logic triggers CXProvider invalidation so Provider to invalidate nil
FYI, I would recommend destroying ALL of your call "infrastructure", not just your individual provider. Case in point, it's possible CXProvider can persist (see below) and you don't want to confuse whatever is acting as its delegate.
All of that leads to here:
12:45:33_473-730
...
14:25:41_743-3257
That kind of time gap indicates that your app suspended, in which case I'd probably use the opportunity to simply exit() and reset the entire app. That is, at the point your app "realized" it was going to suspend post-providerDidReset, you can call "exit()" and terminate yourself. Your app will then be relaunched the next time your app receives a VoIP push.
This obviously isn't the most "elegant" solution, but the advantage is it basically removes the need to try and understand or adapt to whatever is happening to the larger system. Again, this is only happening because callservicesd has already crashed, something you really have no good way to predict or anticipate. If the user can't "see" your app, then you'll never know you exited and your future relaunch means you’re starting from a "clean" state at the point you actually have work to do.
The one thing I'd highlight here is that your primary goal here generally ISN'T "make things work". A provider failure has already destroyed your active call and it generally isn't possible to rebuild that call in a way that will feel/look right to the user. Your main goal here is actually making sure that your app doesn't STAY broken, which is why calling exit may be a better approach.
- Such hard destiny for 0x10e0463c0 is observed first time. Maybe it is iOS bug. You can refer to retain cycles on our side but other providers seem to be retained/released properly. But maybe any other explanations?
CXProvider is part of a cluster of classes which manage both its XPC connection to callservicesd and the local CXCall objects your app uses to manage its calls. Depending on the state of things when that failure occurs, it's possible that your local CXCalls may keep the provider active.
Notably, on this point:
- Logic to end call in CallKit is based on reason. If reason, then use reportCall(with: endedAt: reason:) otherwise CXEndCallAction is used. Strange pattern is observed not the first time. iOS calls providerDidReset for a first CXProvider. And after a few calls, func reportCall(with UUID: UUID, endedAt dateEnded: Date?, reason endedReason: CXCallEndedReason) does NOT end call.call.UUID is proper. Would be nice to read any explanation.
If callservicesd relaunched, then it doesn't know what calls you previously had active, which can basically leave your app tracking dead calls. You can try to end them yourself, but again, if you're in the background then I'm not sure calling exit() isn't the better option.
__
Kevin Elliott
DTS Engineer, CoreOS/Hardware