VOIP notifications - Bandyer/Bandyer-iOS-SDK GitHub Wiki

Enabling VoIP push notifications is the only way your users can receive incoming calls while your application is not active in foreground. In this document we will guide you through the steps needed to enable and support VoIP notifications in your app.

This guide shows you how to enable VoIP push notifications while using the Kaleyra Video SDK 3.0 version. If you need the guide for the previous versions of the Kaleyra Video SDK please refer to the this guide instead.

Table of Contents:

Requirements

VoIP push notifications requires CallKit framework. If you haven't enabled CallKit in your app yet, head over to our CallKit integration guide before reading any further.

The server side

The Kaleyra Video platform does not handle the delivery of notifications on behalf of your application. It is up to you how to deliver notifications to your app. The Kaleyra Video platform provides a set of web hooks you can subscribe to with your servers, in order to be notified about call events, such as calls being created, ended and so on. Here below you'll find the bare minimum steps your server side code must perform in order to deliver voip push notifications to your app:

  • Listen on "on_call_incoming" webhook coming from Kaleyra Video. (Discover how your backend can be notified with upcoming call events registering event hook on_call_incoming here)
  • Forward the data you have received via the webhook to APNS

The client side

The following chapters will guide you through the steps you should do in order to enable VoIP notifications in your app using the Kaleyra Video SDK.

Project setup

First of all, you must setup your project adding to your app target the push notification capability. You must enable push notification capability even if your app does not support regular push notifications, otherwise you won't receive VoIP push notifications either. If you have already set up your app to support regular push notifications, you can skip this chapter altogether.

To enable push notifications in your app, open Xcode and select your app project file in the "project navigator" panel on the left side of your screen. Then, select your app target in Xcode main panel and select the "Signing & Capabilities" tab.

push-notifications-step-1

Then click on the "+ Capability" button in the upper left corner of the "Signing & Capabilities" tab, it should appear a dialog with a list of capabilities you can add to your app (if "Push Notifications" is not listed in the capabilities dialog, then you have already added the Push Notifications capability to your app). Finally double click on the "Push Notifications" entry and you are good to go.

push-notifications-step-2

Eventually an entitlement file should have been added to your project and a "Push Notifications" capability entry should have been added in Xcode's "Signing & Capabilities" tab, like in the following screenshot.

push-notifications-step-3

VoIP management strategies

Starting from 2.9.0 version the Kaleyra Video SDK provides two strategies for handling VoIP notifications received by your app: automatic and manual. The former, automatically handle the reception of VoIP notifications on behalf of your app. The latter, allows you to take control of the VoIP notifications received by your app at the cost of writing the code required to handle VoIP notifications properly. The manual strategy is the default, you can opt-in for the automatic strategy before the Kaleyra Video SDK is initialized, for further information checkout the dedicated section in this document.

Automatic strategy

When the Kaleyra Video SDK uses the automatic strategy, your code is required to perform only two tasks. First, configure the Kaleyra Video SDK providing the information it needs to handle the notification payload for you. Second, send the device push token received from the operating system to your server.

Automatic configuration

You tell the Kaleyra Video SDK to use an automatic VoIP handling strategy using the builders provided. You are required to provide an object conforming to the PKPushRegistryDelegate protocol as argument to the automatic builder method. The provided object will be used by the Kaleyra Video SDK when setting up VoIP push notifications and it is responsible for handling the device push token received by the operating system.

The following snippets of code will show you how the Kaleyra Video SDK should be configured:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // [...] 

    let config = try! ConfigBuilder(appID: "my app id", environment: .production, region: .europe)
                  .voip { voip in
                          voip.automatic(pushRegistryDelegate: self)
                  }.build()
    BandyerSDK.instance.configure(config)

    return true
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    //[...]

    BDKConfig *config = BDKConfigBuilder.create(@"My app id", BDKEnvironmentSandbox, BDKRegionIndia)
                                      .voip(^(BDKVoIPPushConfigurationBuilder * voip) {
                                              voip.automatic(self, nil);
                                      }).build();
    [BandyerSDK.instance configure:config];

    return YES;
}
Getting the device push token

Beware, VoIP device push tokens are not regular push tokens, they look the same but they are not. If your app uses regular push tokens and you are integrating VoIP push also, you are going to get two push device tokens, one for the regular push notifications (got from your app delegate's application(_:didRegisterForRemoteNotificationsWithDeviceToken:) method) and one for the VoIP push notifications (got from the PKPushRegistryDelegate pushRegistry(_:didUpdate:for:) method).

The object conforming to PKPushRegistryDelegate you provided during Kaleyra Video SDK configuration will be called whenever the VoIP device push token is updated by the system. It is up to you to deliver the token received to your back-end system. Here a simple snippet of code showing you how the PKPushRegistryDelegate looks like:

func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
    let token = pushCredentials.tokenAsString
    //Send the device token to your notification delivery system
}
- (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)pushCredentials forType:(PKPushType)type
{
    NSString *token = pushCredentials.bdk_tokenAsString;
    //Send the device token to your notification delivery system
}

Beware, the PKPushRegistryDelegate methods won't be called unless the Kaleyra Video has been connected. You MUST connect the SDK in order to receive the device push token, calling the configure(_:) method on the BandyerSDK singleton instance is not sufficient to get the token.

Receiving VoIP notifications

You should not do anything to receive VoIP notifications, the Kaleyra Video SDK will handle that for you. Once a VoIP notification is received, the iOS operating system will wake-up or launch your app in background (it depends whether your app is in suspended state or not), so your app delegate application(_:didFinishLaunchingWithOptions:) method will be called (only if the app was suspended actually), the root view controller will be loaded and installed in your app main window, your app will execute the same flow it would execute if it was launched by the user from the springboard. Once the Kaleyra Video SDK call client is started, the VoIP notification will be handed to your app and processed by the Kaleyra Video SDK. The only thing you must do is present the call user interface when the IncomingCallObserver callClient:didReceiveIncomingCall: method is called

extension MyViewController: IncomingCallObserver {

    func callClient(_ client: CallClient, didReceiveIncomingCall call: Call) {
        let intent = HandleIncomingCallIntent(call: call)
	
	//Present the call UI interface
	//[...]
    }
}
@interface MyViewController() <BDKIncomingCallObserver>
@end

@implementation MyViewController

- (void)callClient:(id <BDKCallClient>)client didReceiveIncomingCall:(id <BDKCall>)call
{
    BDKHandleIncomingCallIntent *intent = [[BDKHandleIncomingCallIntent alloc] initWithCall:call];

    //Present the call UI interface
    //[...]	
}

@end
Receiving VoIP notifications in foreground

While configuring the VoIP notifications handling strategy to automatic, you can provide a boolean flag indicating whether the Kaleyra Video SDK should listen for VoIP notifications when your app is in background and when it is in foreground. By default, the Kaleyra Video SDK won't listen for VoIP notifications while your app is in foreground. However, if you want to enable this feature you can tell the SDK to do so while configuring it.

The following snippets of code will how you'd do it:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // [...] 

    let config = try! ConfigBuilder(appID: "my app id", environment: .production, region: .europe)
                  .voip { voip in
                          voip.automatic(pushRegistryDelegate: self, listenForNotificationsInForeground: true)
                  }.build()
    BandyerSDK.instance.configure(config)

    return true
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    //[...]

    BDKConfig *config = BDKConfigBuilder.create(@"My app id", BDKEnvironmentSandbox, BDKRegionIndia)
                                      .voip(^(BDKVoIPPushConfigurationBuilder * voip) {
                                              voip.automaticBackgroundAndForeground(self, nil);
                                      }).build();
    [BandyerSDK.instance configure:config];

    return YES;
}

Manual strategy

Below you'll find the required steps you should take if you choose to opt-in for handling VoIP notifications yourself.

Beware, the manual VoIP management strategy is the default.

Manual configuration

Before the Kaleyra Video SDK can be initialized you must tell you want to opt-in for the manual VoIP management strategy:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // [...] 

    let config = try! ConfigBuilder(appID: "my app id", environment: .production, region: .europe)
                  .voip { voip in
                          voip.manual()
                  }.build()
    BandyerSDK.instance.configure(config)

    return true
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    //[...]

    BDKConfig *config = BDKConfigBuilder.create(@"My app id", BDKEnvironmentSandbox, BDKRegionIndia)
                                      .voip(^(BDKVoIPPushConfigurationBuilder * voip) {
                                              voip.manual();
                                      }).build();

    [BandyerSDK.instance configure:config];

    return YES;
}
VoIP notification hand over

Once your app has received a VoIP notification you must hand it over to the Kaleyra Video SDK. Beware, you must call the BandyerSDK.instance.connect(_:) method before the push payload can be handed over to the SDK.

class MyNotificationHandler: PKPushRegistryDelegate {

    func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
         BandyerSDK.instance.handleNotification(payload)
	 completion()
    }
}
@interface MyNotificationHandler() <PKPushRegistryDelegate>
@end

@implementation MyNotificationHandler

- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(PKPushType)type withCompletionHandler:(void (^)(void))completion
{
    [BandyerSDK.instance handleNotification:payload];
     completion();
}

@end
Show the call UI

The last thing you must do is present the call user interface when the IncomingCallObserver callClient:didReceiveIncomingCall: method is called. The Kaleyra Video SDK will invoke the above method when the PKPushPayload received contains information for an incoming call.

Requirements

When choosing to opt-in for the manual VoIP management there are a few requirements to be met by your app in order for the Kaleyra Video SDK to play nice with CallKit.

I) Initialize the PKPushRegistry object providing it a background serial queue. Do not use the main queue nor provide nil as the PKPushRegistry initializer argument

func foo() {
    // DO THIS
    let queue = DispatchQueue(label: "foobar")
    let registry = PKPushRegistry(queue: queue)

    // DO NOT DO THIS
    let registry = PKPushRegistry(queue: .main)
   
    // NOR THIS
    let registry = PKPushRegistry(queue: nil)
}

II) VoIP notifications should be handled only when your app is in background. When the app is in foreground the Kaleyra Video SDK will receive incoming calls through a direct connection with our back-end. However, if you'd like to receive VoIP notifications when the app is in foreground and you'd like the SDK to not receive incoming calls from the direct connection you must tell the Kaleyra Video SDK you want so. While configuring the SDK you must call the ConfigBuilder object to disable direct incoming calls.

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // [...] 

    let config = try! ConfigBuilder(appID: "my app id", environment: .production, region: .europe)
                  .voip { voip in
                          voip.manual()
                  }
                  .disableDirectIncomingCalls()
                  .build()
    BandyerSDK.instance.configure(config)

    return true
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    //[...]

    BDKConfig *config = BDKConfigBuilder.create(@"My app id", BDKEnvironmentSandbox, BDKRegionIndia)
                                      .voip(^(BDKVoIPPushConfigurationBuilder * voip) {
                                              voip.manual();
                                      })
                                      .disableDirectIncomingCalls()
                                      .build();

    [BandyerSDK.instance configure:config];

    return YES;
}

Notification payload key path

The voip push notification payload received by your app is just a simple dictionary containing the information your server sent to APNS. It looks like this:

{
  "aps": {
    "alert": {
      "title": ""
    },
    "content-available": 1
  },
  "event": "on_call_incoming",
  "room_id": "room_58fee601fcef",
  "data": {
    "initiator": "usr_123456789",
    "roomAlias": "room_58fee601fcef",
    "options": {
      "callType": "audio_video",
      "creationDate": "2020-06-26T10:59:04.315Z",
      "duration": 0,
      "recordingType":"automatic",
      "recording":"automatic",
      "live": true,
      "record": false
    },
    "users": [
      {
        "status": "invited",
        "user": {
          "userAlias": "usr_987654321"
        }
      },
      {
        "status": "invited",
        "user": {
          "userAlias": "usr_123456789"
        }
      }
    ]
  }
}

When a VoIP notification is received, the Kaleyra Video SDK will search for the actual payload it needs to create an incoming call. It'll do that automatically, but you can help it out giving it the path it must look for the actual incoming call payload, through the APIs provided on the VoIP configuration builders.
You are not required to provide this value, but if you do it, the Kaleyra Video SDK will use the keypath provided when searching for the actual notification payload.

The Kaleyra Video SDK cares about information in the following format:

{
  "initiator": "usr_123456789",
  "roomAlias": "room_58fee601fcef",
  "options": {
    "callType": "audio_video",
    "creationDate": "2020-06-26T10:59:04.315Z",
    "duration": 0,
    "recordingType":"automatic",
    "recording":"automatic",
    "live": true,
    "record": false
  },
  "users": [
    {
      "status": "invited",
      "user": {
        "userAlias": "usr_987654321"
      }
    },
    {
      "status": "invited",
      "user": {
        "userAlias": "usr_123456789"
      }
    }
  ]
}   

Sample apps

We created two apps, one in objective-c and one in swift to show you how to integrate the Kaleyra Video with support for VoIP push notifications in your app. The two apps show you how to enable CallKit and VoIP push notifications because they go hand to hand. Starting from iOS 13, VoIP push notifications require CallKit to work.

Where to go from here

You should now have a better understanding of how to integrate VoIP push notifications in your app. If you haven't already you should take a look at our Receiving an incoming call guide that will show you how to handle an incoming call in your app.

⚠️ **GitHub.com Fallback** ⚠️