Skip to content
This repository has been archived by the owner on Dec 6, 2023. It is now read-only.

IOS 13 voIP restriction #197

Open
mudassirzulfiqar opened this issue Sep 26, 2019 · 40 comments
Open

IOS 13 voIP restriction #197

mudassirzulfiqar opened this issue Sep 26, 2019 · 40 comments

Comments

@mudassirzulfiqar
Copy link

Because of new ios 13 voip restrictions
func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void)
completion should be called with in the same loop for - (void)reportIncomingCall:(VSLCall *)call .
Any idea how this new restriction will be implemented with this library.

@ismaiI1
Copy link

ismaiI1 commented Sep 26, 2019

@mudassirzulfiqar Did you get VoIP push notification in background? I couldn't.. When app in foreground didReceiveIncomingPushWith is called, but in background it's not called..

@mudassirzulfiqar
Copy link
Author

@Invisible66 Right after posting this, I also find this weird thing that didReceiveIncomingPushWith is not called when the app is is background. But I think I figured this out why, but let me investigate this more.

@Florianlec
Copy link

@mudassirzulfiqar Same behavior for me here, for some reasons I do receive calls in foreground and didReceiveIncomingPushWith is getting called, but not in background.
I'm also on it.

@Florianlec
Copy link

Interesting post from Apple staff about that : https://forums.developer.apple.com/thread/117939

@mudassirzulfiqar
Copy link
Author

I have figured out the issue, as it is stated in the official forums by Apple that not calling completion will result ban your application after 3 to 5 attempts I guess. So I think when the app get banned in 2 to 3 attempts then it stops receiving voip.

Use case:
I reinstalled my application and put my application into the background and
didReceiveIncomingPushWith gets called. But because i'm not using completion closure on the right time, probably again Im not going to receive voip next time.
And this always work fine when app is in foreground.

@ismaiI1
Copy link

ismaiI1 commented Sep 26, 2019

Interesting post from Apple staff about that : https://forums.developer.apple.com/thread/117939

I didn't understand anything from this post..

@ismaiI1
Copy link

ismaiI1 commented Sep 26, 2019

I have figured out the issue, as it is stated in the official forums by Apple that not calling completion will result ban your application after 3 to 5 attempts I guess. So I think when the app get banned in 2 to 3 attempts then it stops receiving voip.

Use case:
I reinstalled my application and put my application into the background and
didReceiveIncomingPushWith gets called. But because i'm not using completion closure on the right time, probably again Im not going to receive voip next time.
And this always work fine when app is in foreground.

Perfect. Thank you, you save my day :)
This means, we have to call reportNewIncomingCallWithUUID in didReceiveIncomingPushWith.

@Florianlec
Copy link

@Invisible66 that sounds good indeed, but what if the account is not registered to SIP ?

@ismaiI1
Copy link

ismaiI1 commented Sep 26, 2019

so, we have to end call using reportCallWithUUID:endedAtDate:reason: as Apple staff says.

@mudassirzulfiqar
Copy link
Author

@Invisible66 that sounds good indeed, but what if the account is not registered to SIP ?

Im also stuck on achieving this behaviour, in our application, we always push new account information in payload to register the sip calls, and obviously for this we have to call so many library methods. So Im currently unable to call this completion() closure.

I checked multiple apps like Slack, Skype, Messenger. They all are using old way to initiating the sip calls using notification technique. Only WhatsApp is managing this callkit thing, but I also faced some issues on receiving whatsapp call from my friend. (Did some testing.)

@pshitikov
Copy link

This problem is relevant to me. Any suggestions on how to resolve this issue?

@ismaiI1
Copy link

ismaiI1 commented Oct 1, 2019

This problem is relevant to me. Any suggestions on how to resolve this issue?

I am looking for any solution.. If you find something, can you share it with us please? :)

@mudassirzulfiqar
Copy link
Author

We have reverted back to old implementation, which is clicking the notification and opening the app.

@ismaiI1
Copy link

ismaiI1 commented Oct 8, 2019

We have reverted back to old implementation, which is clicking the notification and opening the app.

We post call to Callkit in didReceiveIncomingPushWith. After registration, we update call information in reportNewIncomingCall's completion. you have to set call's UUID before call information update.

This is a bad solution, but it's better than clicking notification and opening the app.

@mudassirzulfiqar
Copy link
Author

We have reverted back to old implementation, which is clicking the notification and opening the app.

We post call to Callkit in didReceiveIncomingPushWith. After registration, we update call information in reportNewIncomingCall's completion. you have to set call's UUID before call information update.

This is a bad solution, but it's better than clicking notification and opening the app.

My question is how can you post a call without even registering the sip account, and without even receiving the call? What information do you send in the voip?
Can you please share
thanks

@ismaiI1
Copy link

ismaiI1 commented Oct 8, 2019

We have reverted back to old implementation, which is clicking the notification and opening the app.

We post call to Callkit in didReceiveIncomingPushWith. After registration, we update call information in reportNewIncomingCall's completion. you have to set call's UUID before call information update.
This is a bad solution, but it's better than clicking notification and opening the app.

My question is how can you post a call without even registering the sip account, and without even receiving the call? What information do you send in the voip?
Can you please share
thanks

I did as described on this page.
https://developer.apple.com/documentation/pushkit/responding_to_voip_notifications_from_pushkit?language=objc

@www0488
Copy link

www0488 commented Oct 9, 2019

I have figured out the issue, as it is stated in the official forums by Apple that not calling completion will result ban your application after 3 to 5 attempts I guess. So I think when the app get banned in 2 to 3 attempts then it stops receiving voip.

Use case:
I reinstalled my application and put my application into the background and
didReceiveIncomingPushWith gets called. But because i'm not using completion closure on the right time, probably again Im not going to receive voip next time.
And this always work fine when app is in foreground.

I tested to reinstall the app, but I still cannot get any response from the "didReceiveRemoteNotification" method and this drives me crazy.

I even tried to restart the device but it does not work.

Is there anyone who have the same issue and know how to solve it?

@muhammadalkhalaf
Copy link

We have reverted back to old implementation, which is clicking the notification and opening the app.

We post call to Callkit in didReceiveIncomingPushWith. After registration, we update call information in reportNewIncomingCall's completion. you have to set call's UUID before call information update.

This is a bad solution, but it's better than clicking notification and opening the app.

Can you explain this solution please ?

@ismaiI1
Copy link

ismaiI1 commented Jan 22, 2020

We have reverted back to old implementation, which is clicking the notification and opening the app.

We post call to Callkit in didReceiveIncomingPushWith. After registration, we update call information in reportNewIncomingCall's completion. you have to set call's UUID before call information update.
This is a bad solution, but it's better than clicking notification and opening the app.

Can you explain this solution please ?

  • When we received voip push notification, We repoted a new fake incoming call using CallKitProviderDelegate.provider. We created new NSUUID string. And we called setupCallKit and setupVialerEndpoint.

  • When SIP registration completed, [VialerSIPLib sharedInstance].incomingCallBlock called and we received the real call. Now we have to update CallKit screen with real caller informations.

  • in [VialerSIPLib sharedInstance].incomingCallBlock, We set VSLCall object's uuid property. And we called [providerDelegate reportIncomingCall:call];.

  • in [CallKitProviderDelegate reportIncomingCall] method, we updated the call information using uuid property.

I hope, you can understand my bad English :)

@Muhammad-AhmadRafique
Copy link

Hi All,
Can anyone help me out to resolve this iOS 13 weird thing with this library. ?
When I receive Voip push, I open a dummy CallKit UI, and when actual call come, I call this function:-
let reason = CXCallEndedReason.init(rawValue: CXCallEndedReason.failed.rawValue)
provider.reportCall(with: uuid, endedAt: nil, reason: reason!)

and it tears down the dummy CallKit UI, but it show very bad behaviour of the UI, as WhatsApp and other apps don't show this behaviour.

Any suggestion how to resolve this issue.
I am stuck at this point for almost 3 weeks, somebody please help me.

Thanks,

@mudassirzulfiqar
Copy link
Author

@Muhammad-AhmadRafique
Send call information in voip to iOS app.
But due to iOS 13 you need to immediately present incoming call UI to the screen. Otherwise your AppDelegate will get crashed.
While you prompt the callerId information for example in your voip payload you send useful data like “caller id , caller name”. You register the PJSIP using this vialer library.
And after registering user account you can immediately update the call manager with the ongoing call.

Please read below links for solutions:

https://forums.developer.apple.com/thread/117939
#207 (comment)

@Muhammad-AhmadRafique
Copy link

@mudassirzulfiqar thanks for the reply.
But basically the issue is this, voip comes first and a fake call UI opens, and after that, actual call comes and another UI opens, which comes from CallkitProviderdelegate -> reportIncomingCall.

How can I handle both of the screens simultaneously,
Currently when actual call comes, I ended the dummy call UI forcefully, but this is not a good approach.

I have also read the above thread,
but with this library, I don't know how to update that dummy UI when push comes?
please help me to resolve this.

Thanks again for your help.

Regards,
Mohammad Ahmad

@mudassirzulfiqar
Copy link
Author

mudassirzulfiqar commented Mar 19, 2020

@Muhammad-AhmadRafique This library totally supports this new ios 13 voip limitation.
The links which I shared above has the complete knowledge about handling this scenario, Why you are opening two UI?

  • Just Show a Call Kit UI when there is a voip.
  • While showing a UI write a logic to update current callStatus using callManager objects, in the completion closure of reportNewIncomingCall.

below is the code you are looking for:
#207 (comment)

@Muhammad-AhmadRafique
Copy link

@mudassirzulfiqar Thanks again for your help.
I have updated my code when voip push received, as per the link.
But one more thing I want to ask,
when actual call comes through asterisk, do I need to update my appDelegate.reportIncomingCall() method in CallkitProviderDelegate.m file ?
And if it must be updated, then what code should I write in that method. ?

or otherwise, it will work fine , I have to only change in voip push method, which you suggested earlier ?

Regards,

@mudassirzulfiqar
Copy link
Author

I did not understand the question, but let me tell you the flow

  • When you call this method appDelegate.callKitProviderDelegate.provider.reportNewIncomingCall. This actually shows a call Kit UI on the screen.
  • It takes less then one second to register SIP in the app level, and then on the completion Handler, You update the callManager like this:
let callManager =  VialerSIPLib.sharedInstance().callManager
                    callManager.add(newCall)

This is described above in the link as well.

I did not understand this

when actual call comes through asterisk, do I need to update my appDelegate.reportIncomingCall() method in CallkitProviderDelegate.m file ?

@Muhammad-AhmadRafique
Copy link

ok, let me tell you the flow of my app, maybe you understand in a better way and guide me what I am doing wrong.

In my app, I am doing following steps, and it was working fine unless iOS 13:-
STEP 1 First user calls a rest api, which sends voip pushes to other iPhone device and other user uses this voip push method body to only register to Sip server,
STEP 2 This rest api keeps on checking either other user is connected to Sip or not, if it is not connected, it keeps on sending push after every 4 second if the other user does not connect to the Sip server within these 4 seconds.
STEP 3 if after 4 seconds, other user becomes online, then this api returns response to the first user that other user becomes online,
STEP 4 And then first user makes a call through asterisk server to other user.

The following steps were working fine, till iOS 12.

But for iOS 13, what limitations I am facing right now:-
For STEP 1
I must have to open a fake dummy UI when voip push receives which was not the case previously.
For STEP 2
If other user does not connect to sip client for any network problem in 4 seconds, another push comes and another call UI opens (as per iOS limitation, we have to open dummy Call UI for each voip push received)
For STEP 4
now first user gets response that other user becomes online and it calls other user through asterisk server,

So in other user iPhone device, 1 or more Call UIs open already due to voip pushes and now actual call UI opens , so there are multiple UIs are open in other user's device.

Thats why I am asking you what am I doing wrong. ? please guide me.

Now I am asking you following question ?

  • Should asterisk server itself call voip push script ? because for now, I am calling rest api to do it. ( is it good approach to call rest api to send voip push ) ?
  • How to manage multiple UIs in other user's iPhone device, as per above scenario.

I am stucked at this, please help me.
If you need any assistance regarding my above flow, please tell me, I will explain in more detail.

Once again thank you so much for your cooperation and help.

Kind Regards,
Ahmad

@mudassirzulfiqar
Copy link
Author

Your first four steps which you described does not seem to me the proper implementation, The voip notifications were meant to be for Incoming Calls only. Because of this abusive behaviour which you also described that it pings the device after some interval, Apple now forcefully want you to show the UI.

It is not only you who suffered from all these thing, many big voip applications had forcefully changed their backend implementation along with the front-end

If you read the some other third party voip call providers for example Twilio. You will find out that they also had to adopt the new ios 13 behaviour and their sample ios application for voip does the same flow which Im describing to you.

About your second four steps, you have to change the whole implementation to adopt this ios 13 thing. Im not very expert in backend but still I see that your approach for pinging a device is not correct. Which makes you open multiple UI, its totally not a valid approach. No one does that.

There is no need to keep checking the device by sending voip notifications, I think you can play with asterisk and query the connection from backend for receiving client rather then the client applications. You should not care about is the device connected or not it the backend thing. How can one person be offline?, if logically the client device is offline then dont send voip. And with this new implementation the ios code has to show the UI, so there is no case when the person is offline.

Should asterisk server itself call voip push script ? because for now, I am calling rest api to do it. ( is it good approach to call rest api to send voip push ) ?
I think rest api is okay for that, you can hit the api and present outgoing call screen, meanwhile the receiver will receive the sip information and register itself or try to make connection.

@Muhammad-AhmadRafique
Copy link

Thanks for the response,
Yes, I have heard about many apps related to VOIP calling, and these apps already changed their implementation for iOS 13.

You asked when an iPhone device is offline to asterisk. ?

  • When an app is killed, our asterisk server shows this device becomes offline.

For this :-
I think you can play with asterisk and query the connection from backend for receiving client rather then the client applications.

  • What do you mean ? I don't understand.

For this:-
You should not care about is the device connected or not it the backend thing
You mean, asterisk server must know that device is connected or not ?
if device is connected, so make calls directly or otherwise send voip push to connect first and then make calls ?

@mudassirzulfiqar
Copy link
Author

mudassirzulfiqar commented Mar 20, 2020

When an app is killed, our asterisk server shows this device becomes offline.

Why do you care if the asterisk server shows device offline?
You should not worry about the app is killed, Worry about the endpoint of sip are killed. To solve this you need to write multiple if else to check if sip is connected, if endpoints are connected, if account is already registered, if ...... and execute these statements when there is voip notification.

What do you mean ? I don't understand.

As far as I know you can see the asterisk statuses in the backend.

You mean, asterisk server must know that device is connected or not ?
if device is connected, so make calls directly or otherwise send voip push to connect first and then make calls ?

Dont go for multiple solutions, always send the voip, again you have to write a long method with lots of if else to know what to do.
For example:

  • The app is in foreground, you receive voip. Check whether the sip account is registered, if it is registered then just connect the call.
  • The app is killed , you receive voip, Check again now you will probably see sip account is disconnected, now re connect or re register and do the rest.

While checking all these statuses, show a call kit UI.

@Muhammad-AhmadRafique
Copy link

just connect the call.

How to connect the call ?
Calling happens only when other iPhone user make a call, not otherwise.

This is the point, where I am stuck.

@Muhammad-AhmadRafique
Copy link

Actual calling happens only when other user make a call through asterisk.

What are you suggesting is that,
only voip will do the connection thing and also actual call thing (both must be done through voip),

I don't understand this point.
Sorry for not understanding your point.

@mudassirzulfiqar
Copy link
Author

  • Press Call Icon.
  • Rest Api hit (VOIP received whom you are calling). (Not you are receiving voip)
  • Other persons received voip. Tried to register.
  • Registered Successfully.
  • Now other person update the callManager.
  • You both connected to a call.

@Muhammad-AhmadRafique
Copy link

@mudassirzulfiqar let me tell you something.
I am only stuck on last point.

  • Press Call Icon.
  • Rest Api hit (VOIP received whom you are calling). (Not you are receiving voip)
  • Other persons received voip. Tried to register.
  • Registered Successfully.
  • Now other person update the callManager.

(stuck on it)

  • You both connected to a call.

Because, When first user call the following function, then actual call comes to 2nd user. and first user only call this method, when rest api response comes to the first user.

self.callManager?.startCall(toNumber: self.callingId, for: self.account!, completion: { (call, error) in
if let err = error {
print(err.localizedDescription)
}
})

@Muhammad-AhmadRafique
Copy link

Without calling startCall:ToNumber function, how actual call comes to 2nd user ?

Because, when voip push received, 2nd user registers to SIP client, and update its callManager with the parameters received from voip push and call reportIncomingCall method, and a CallKit UI opens.

Then first user call this method startCall:ToNumber, and actual call comes to 2nd user,
so How can we interlink this actual call with the above one when Voip Push received.

Once again, thanks for your help.
Regards,

@aabiawan
Copy link

aabiawan commented Mar 8, 2021

@mudassirzulfiqar kindly make a proper video on it , as it is very important issue rightnow

@mudassirzulfiqar
Copy link
Author

@mudassirzulfiqar kindly make a proper video on it , as it is very important issue rightnow

@aabiawan Hi , What exactly you need to know ?

@aabiawan
Copy link

aabiawan commented Mar 8, 2021 via email

@mudassirzulfiqar
Copy link
Author

@aabiawan You need to study Apple VOIP notification

@aabiawan
Copy link

aabiawan commented Mar 9, 2021 via email

@mudassirzulfiqar
Copy link
Author

@aabiawan There are different ways to implement both incoming calls and dialling.

  • You need to have account details for the signed in person. Username/Password/Host/PORT etc
  • When you are connected then you can dial a number using this framework , and it handles everything for you.
  • For Receiving a call its little complicated, The moment you will receive a call. Backend should send you a silent voip notification , that voip notification should include every information related to call , so you can register the user's account.

Official Running application using this framework : https://github.com/VoIPGRID/vialer-ios
You can easily see how they are doing it.

This is the middleware repo which handles this voip thingy.
https://github.com/VoIPGRID/vialer-middleware

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants