Switching from CallKit UI to in-app UI - ios

I have a VoIP app. It used to work without CallKit, so it has its own in-app call UI. I'm now integrating CallKit UI to handle calls which happen when the app is not running, and have a question:
What is the recommended way to detect a moment when the CallKit UI is dismissed and is going to switch to the in-app UI?
Currently I see that the CallKit UI disappears in 2 cases:
when the app process is not running, and the device is unlocked (e.g. on the home screen) and the call arrives, the CallKit UI appears at first, but if you accept the call (answer), then the CallKit UI disappears immediately, and the app appears.
when the app process is not running, and the device is locked, the call arrives, the CallKit UI appears, you accept the call (answer), and then press the app icon in the CallKit UI, then again the CallKit UI disappears, and the app appears.
What is the recommended way to detect these conditions in order to spawn the in-app UI?
The reason why I don't want to have the in-app UI started all the time is performance and logic. I think it would be wasteful to show and update the controls and video views (the app has video support) while the CallKit UI is on top of the screen. It would be more logical to me if I could detect that the UI switch is needed, and create a UI at that point.
I have looked over the CallKit API, but found nothing for that purpose.

For the second use case, I have logic in AppDelegate like this:
func applicationWillEnterForeground(_ application: UIApplication) {
maybeShowCallUi()
}
fileprivate func maybeShowCallUi() {
if CallManager.shared.calls.count > 0 {
print("One or more calls active.")
DispatchQueue.main.async() { [weak self] in
print("Trying to show in call UI.")
self?.showInCallViewController()
}
} else {
print("No calls active.")
}
}
My CallManager object is similar to the SpeakerBox sample code that Apple provided. It keeps track of all calls.

After the user Answers the call, app call screen should be visible. For that, inside the following method write your code to make app call screen visible.
- (void)provider:(CXProvider *)provider performAnswerCallAction:(nonnull CXAnswerCallAction *)action {
//show call screen here.
}

Related

Switching from in-app UI to CallKit UI during two ongoing calls with one on hold

I'm working on a Voip app and integrated CallKit. I know the CallKit UI will not show when there's incoming Voip calls, however the problem is when there's an ongoing Voip call and phone call at the same time (one on hold). You can test the below using Whatsapp.
Start Voip call
During the call, someone calls the phone using non internet sim/phone line
CallKit UI shows up, with options to decline, end, or hold the voip call to answer the incoming phone call
Select 'hold and answer', callkit UI shows two calls, with the voip call being hold
Go back to the Voip app either by pressing the swap button and then the app icon (on the callkit UI itself), or by pressing going to home screen and click on app icon
Voip app shows, there's a green bar on top that says 'Touch to return to call', I want to be able to go back to CallKit UI screen when user taps it (same as whatsapp).
If you test this on whatsapp you will know what I mean, I want to achieve the same behaviour.
I have implemented the top green bar following this post:
How to show double height green statusbar (In-Call) in foreground app on device?
However I couldn't find a way to programmatically bring the CallKit UI back to the front, the answer in that post doesn't show how.
This post is similar but there's no answer because for that case there's no other calls on hold hence there's no requirement to bring CallKit to the front: CallKit - How to bring the CXCallController to the front
I found that the info that I got on stackoverflow on this topic were outdated. For the case where there's multiple calls on CallKit, the green bar will show even when your app is in the foreground given that your app is showing the status bar!
Previously it didn't appear on my app because I have set the status bar to be hidden on that particular page where the call is being made... so the solution is to detect when there is call being set to on hold (via callkit delegate) and show the status bar, and hide it when one of the calls ended and there's no more calls on hold.
When user clicks on the green bar while inside your app, it brings user back to the CallKit UI as expected.
First declare variables
var callKitProvider: CXProvider!
var udid = UUID()
then call this function where call will be ended
callKitProvider.reportCall(with: udid, endedAt: Date(), reason: .remoteEnded)
please make sure that same udid used in "report call" and "incoming call"

When to use native CallKit UI and when your own custom telephone UI?

I integrated CallKit successfully in my app, but I'm kind of confused how to use it correctly. When the phone is in locked state, I get the native Call UI like this:
When my app is in background and it receives an incoming call, the following interface I get:
By the way, after I accept the call, it opens my app and brings it to the foreground.
While I'm expecting the native Call UI (CallKit) to stay in the foreground when accepted the call.
However, if my app is brought to the foreground by CallKit, the native Call UI is not gone, but stays in memory. When I double press on the home button to get a list of opened apps, I can see the native Call UI as follows:
So the native Call UI is still there and I can get it to the foreground and control the call. So, what happens is, I get 2 UI's, one is the native Call UI and the other is my own app.
Also, the examples and demos I found about CallKit is confusing, because I only see native Call UI and no custom VoIP app UI. So what's the deal with Call UI? When should it be displayed and when should a custom app UI be displayed?
I know that it seems a bit confusing at first, but it's just the way it works. You don't have control on whether to show the system UI or your custom UI.
While the phone is ringing only the system UI is shown. As soon as you answer, if the phone is locked then the system UI is shown; if the phone is unlocked then your app will become active and your custom UI is shown.

How can I detect lockscreen status to choose between CallKit and LocalNotification for incoming calls?

How to detect phone lockscreen status in a PushKit delegate to choose between notifying the Call to CallKit or using a LocalNotification to notify the user. The aim is to avoid having users actually have to Tap the video button when answering from lockscreen (Because more often then not, test users can't figure it out and get frustrated).
We have a Chat application written in Xamarin that supports audio/video calls over WebRTC.
On iOS incoming calls when the app is in the background or closed are handled using PushKit.
We also implemented CallKit looking to get a more native feel.
But when we receive a call on a locked screen iOS device, after answering CallKit UI stays on top, and we can only move to video call if the user taps the button.
The fact is that CallKit UI answering behavior is inconsistent between Foreground, Background and Locked states, and our users usually can't figure out why they are not getting the App Call UI after answering on locked screen.
We noticed that WhatsApp has worked around the problem by reverting to a visible notification when the screen is locked, and only seems to use CallKit when unlocked, but we can't figure out how to replicate this behavior.
How can we reliably detect Phone lock screen status in our PushKit delegate?
We have tried monitoring screen brightness, but it always gives the same value regardless of state.

How to know when the user has started an Apple Watch app?

I would like to know when the user starts my Apple Watch app (started from menu or from complication). WKInterfaceController's documentation states that the didAppear method is called when the interface controller content is on screen.
In my simple example project I'm logging all calls to the didAppear method, and I see that it gets called also when the app is not visible on screen.
override func didAppear() {
super.didAppear()
log("didAppear") // Triggered when app not visible
}
My guess is that this has to do with snapshot refreshing, but is there any way to know when the user (not the system) has started my app?
Use applicationDidBecomeActive for this.
From Apple Developer Documentation:
WatchKit calls this method to let you know that your app transitioned from the inactive to the active state. Use this method to start any tasks that were paused or not yet started while the app was inactive.

Speakerbox IOS callkit UI hides itself after call receive

After accepting incoming call on speakerbox iOS, the UI which shows caller name and timer on native call controller goes back of the app. Is there a way to take that to the front form where user can disconnect, mute and see the timer.
When your app is in the foreground, it is responsible for showing a UI for the call (for example, what does your app do on iOS 9?). So by bringing your app to the foreground, that is enough.

Resources