Contact usRequest a demo

iOS mobile SDK integration

This section describes how the visitor/agent SDK can be integrated into an iOS app. It describes step by step from adding the framework files to use the API, whereby it gives some hints to get a good user experience and avoid some common pitfalls.

The information here refers to version 4 of the Unblu iOS mobile SDK. Please refer to the Unblu 6 documentation for information on version 3 of the SDK, and Unblu 5 documentation for information on version 2 of the SDK.

Requirements

The required version of iOS depends on the version of both the Unblu server and the iOS mobile SDK you are using:

Table 1. Minimum required version of iOS

Unblu 6

Unblu 7

mobile SDK v3

iOS 12 or newer

iOS 14.5 or newer

mobile SDK v4

N/A

iOS 14.5 or newer

Resource requirements when in use

The total size of the iOS mobile SDK ranges from 5 to 30 MB, depending on the dependencies required for your use case. If your app offers calls using Vonage, the total size is approximately 30 MB. If you use LiveKit to provide the call feature in your app, the total size is approximately 22 MB. Both values take into account the dependencies required.

When running, the SDK uses roughly 25 MB of RAM. Initializing the SDK uses an additional 3—​4 MB of RAM. The CPU usage of the SDK depends heavily on which Unblu feature is in use. Operations within the WebView can result in short peaks of up to 5% CPU usage. For video calls, CPU usage can be 50% or higher, depending on the number of call participants, and RAM usage can increase up to 400 MB.

Network traffic

During initialization, the SDK downloads approximately 0.3 MB of JavaScript files. The UI requires downloading around 2.5 MB of additional resources. Some of these downloads may be cached on the device, resulting in less network traffic for later uses.

For text messaging, only the message itself and the HTTP request overhead is transmitted.

By default, Unblu transmits an image around 5 times per second in mobile co-browsing sessions if the content has changed. You can adjust the maximum frame capture rate with the configuration property com.unblu.mobiledevice.iosMaxFpsRate. The size of the image depends on the size of the mobile device’s screen but is typically 5—​20 KB large.

In audio and video calls, network traffic depends on the quality of the connection, as both Vonage and LiveKit change the codec based on the network connection.

Framework composition

Unblu is divided into modules, and depending on your implementation, you may use all, or only some of them. Each module is distributed as an XCFramework, Apple’s binary framework format.

In total, there are six frameworks. The core framework must always be added:

  • UnbluCoreSDK.xcframework

Additionally, there are optional modules:

  • UnbluMobileCoBrowsingModule.xcframework

  • UnbluOpenTokCallModule.xcframework

  • UnbluLiveKitCallModule.xcframework

  • UnbluCallKitModule.xcframework

  • UnbluFirebaseNotificationModule.xcframework

The table below illustrates the binary type for each Unblu framework:

Unblu Framework Name Binary type

UnbluCoreSDK

dynamic

UnbluMobileCoBrowsingModule

dynamic

UnbluOpenTokCallModule

dynamic

UnbluLiveKitCallModule

dynamic

UnbluCallKitModule

dynamic

UnbluFirebaseNotificationModule

static

For all dynamic frameworks in the "Frameworks, Libraries, and Embedded Content" section of the Xcode project settings, choose either "Embed & Sign" or "Embed Without Signing". For static frameworks, select "Do Not Embed".

To add the files to an Xcode project, you need to add them to your app target. You can obtain the files from the Unblu iOS mobile SDK GitHub repo, where you can also find instructions on installing the Swift package. Alternatively, you can drag and drop the files directly into this section.

You must add some dependencies that aren’t included in the frameworks. Which dependencies you need depends on the Unblu modules you use. The following table shows the dependencies you need to add to your app for different Unblu modules. We recommend adding the dependencies using CocoaPods or Swift packages, but the table includes links to instructions on how to integrate the libraries manually.

Unblu module name Dependency name Version CocoaPods podfile Manual integration link Purpose

UnbluFirebaseNotificationModule

Firebase Core

~> 7.0

pod ‘Firebase/Core’, ‘~> 7.0’

Integrate without CocoaPods

Base for Firebase messaging

UnbluFirebaseNotificationModule

Firebase Messaging

Derived from Firebase Core

pod ‘Firebase/Messaging’

Integrate without CocoaPods

Needed for push notifications

UnbluLiveKitModule

LiveKit WebRTC Provider

="104.5112.08"

pod 'WebRTC-SDK'

Integrate without CocoaPods

Needed for audio and video calls using https:livekit.io[LiveKit^]

For Firebase to work correctly, you must add the GoogleService-Info.plist file from the Firebase project to your app. To do so, please follow the instructions here.

It isn’t necessary to set up Firebase in your code. This is done automatically by the SDK when push notifications are enabled.

Swift compatibility

Objective-C

The frameworks provided by Unblu are written in Swift, not Objective-C, as Swift is the new standard from Apple. In principle Swift code can be used in Objective-C, but there are some limitations. For example, enums in Swift need to have a raw type of Int. It is therefore not simply possible to use the SDKs in an Objective-C environment. If your project is formally an Objective-C project, you must convert it to a Swift project that mainly uses Objective-C. The usage of an SDK must then be wrapped in a Swift helper which in turn can be used in Objective-C. Currently Unblu doesn’t provide any Swift helper classes.

Swift versions

In 2014, Apple wrote:

  1. if your project uses frameworks to share code with an embedded extension, you will want to build the frameworks, app, and extensions together. It would be dangerous to rely upon binary frameworks that use Swift —- especially from third parties. As Swift changes, those frameworks will be incompatible with the rest of your app. When the binary interface stabilizes in a year or two, the Swift runtime will become part of the host OS and this limitation will no longer exist.

Swift is ABI stable as of version 5.0, so if your project is running Swift 5.0 or newer, it should be able to interface with the Unblu SDK. If you require a version of the Unblu SDK built with an older version of Swift, contact the Unblu support team.

Unblu aims to provide the iOS SDKs for the newest Swift version.

Permissions

Various features require that you include certain permissions in your application’s Info.plist.

NSPhotoLibraryAddUsageDescription

Required to add downloaded images to the Photos app

NSPhotoLibraryUsageDescription

Required to upload images from the Photos app to a conversation

NSCameraUsageDescription

Required to upload images taken by the camera into a conversation. It’s also required by OpenTok and LiveKit for audio and video calls.

OpenTok is a dependency of UnbluOpenTokCallModule. LiveKit is a dependency of UnbluLiveKitCallModule.

NSMicrophoneUsageDescription

Required by OpenTok and LiveKit for audio and video sessions.

OpenTok is a dependency of UnbluOpenTokCallModule. LiveKit is a dependency of UnbluLiveKitCallModule.

If a user permanently denies access to the camera, microphone, or speakers when the SDK requests it, the SDK won’t display a popup requesting permission anymore. To overcome this, the user must change the settings of their device, either reverting the access denial or granting access directly.

You should also enable the following background modes:

Audio, Airplay, Picture in Picture, and Voice over IP

Required to continue calls when your app is in the background

Remote notifications

Required to receive all push notifications

You must specify these functions in the app description you provide for the AppStore verification process.

Code setup and initialization

Before using the Unblu SDK in your code, you need to add the statement import UnbluCoreSDK to any Swift file that requires it.

Unblu instance

The two most important Unblu instance types you can create using UnbluCoreSDK are:

Both types inherit from UnbluClient which provides shared functionality such as start and stop.

To create an instance of either UnbluVisitorClient or UnbluAgentClient, use the static functions Unblu.createVisitorClient and Unblu.createAgentClient respectively. The Unblu instance created this way is not a singleton. You must implement your own solution for managing the lifecycle of the Unblu instance.

It is highly recommended to create an instance of Unblu inside the AppDelegate.application function and then pass this around your application where it is required.

In theory, you can have more than one Unblu instance running at the same time. However, it is strongly discouraged because Unblu isn’t designed to work this way.

Unblu client configuration

When creating an instance of the UnbluVisitorClient or UnbluAgentClient, you need to provide a configuration. This is done by passing an instance of UnbluClientConfiguration to the Unblu.createVisitorClient and Unblu.createAgentClient functions, respectively. The UnbluClientConfiguration struct can be instantiated with the base URL of the Unblu server and the API key of the Unblu account.

For all possible configurations and their effects, take a look at the documentation of UnbluClientConfiguration. Only a limited number of settings can be configured via the Unblu SDKs. All configurations which are independent of the client (the app) can be done via the Unblu Account Configuration interface.

To properly separate the configuration for the mobile visitor API, the mobile agent API, and the webpage where Unblu is integrated, we recommend that you use different API keys for each environment.

The Unblu client instance only stores a copy of the configuration, so you can’t change the configuration via the reference alone. To reconfigure Unblu, you must create a new Unblu instance and pass in your new UnbluClientConfiguration.

Authentication

If you need to pass authentication information to the Unblu iOS mobile SDK, you can either provide them using UnbluClientConfiguration.accessToken. Alternatively, you can set custom cookies.

Authorization

If the user is authenticated against an external OAuth 2.0 authentication server that provides the application with an authorization token, the use of this token for authorization is divided into two stages:

If the token isn’t valid, an error is received in unblu(didReceiveError:description:). The error type is UnbluClientErrorType.authorization. When this occurs, you must stop the API, assign a valid token, and start the API again.

How to implement the Oauth 2.0 protocol in your iOS application — get a token, refresh it, select a refreshing interval etc. — is outside the scope of this documentation. You can find more information related to this topic in the Apple documentation:

Unblu client delegate

The Unblu client delivers events via delegation.

Both protocols inherit shared functionality from the UnbluClientDelegate protocol. The functions to adopt in these protocols are all optional and are there to inform you of specific events so you can take certain actions.

Make sure to assign your delegate before you call UnbluClient.start.

One of the more important delegate functions to be aware of is unblu(didRequestShowUi reason: UnbluUiRequestReason, requestedByUser: Bool). If you are using push notifications, we strongly recommend that you adopt this function in your implementation. Suppose there is a push notification when there is an incoming call. If the user opens the app by clicking the notification, Unblu will call this function and assume that the app will display the UnbluView as soon as possible.

Starting and stopping

The UnbluClient instance provides functions called UnbluClient.start and UnbluClient.stop to start and stop the connection, respectively. As these functions are defined on the UnbluClient protocol, they are available on both the UnbluVisitorClient and UnbluAgentClient.

Both functions accept a completion block that is called with a Result type containing success or an error, depending on the outcome.

If the Result represents a success, the UnbluView will start rendering Unblu content, you can start new conversations, check agent availability etc.

Note that how quickly Unblu is ready to be used after it is started depends on the user’s connection speed. If you are using push notifications, it is also important to have started Unblu at least once, so that a deviceToken (required for APNS connectivity) can be delivered to the Unblu server. Without the deviceToken, the Unblu server has no way to determine the link between a user and their device. Therefore, in general we recommend that you start Unblu as soon as possible, ideally directly after login.

If Unblu is no longer needed you should call the UnbluClient.stop function. This will terminate the connection and clean up resources.

Modules

Unblu provides three modules that extend the default functionality provided by the UnbluCoreSDK:

  • UnbluMobileCoBrowsingModule

  • UnbluCallModule

  • UnbluCallKitModule

  • UnbluFirebaseNotificationModule

The modules correspond to XCFrameworks of the same name, except for UnbluCallModule, which is embedded in CoreSDK. UnbluCallModule then uses OpenTok or LiveKit dynamically, depending on the configuration of the Collaboration Server.

To use the UnbluMobileCoBrowsingModule and UnbluCallModule, you must register them with the UnbluClientConfiguration before you create your Unblu instance. You do this by creating an instance of the module and registering it via the UnbluClientConfiguration.register function.

UnbluCallModule

The UnbluCallModule is a bridge between CoreSDK and different WebRTC providers. It’s embedded in CoreSDK. The XCFrameworks named UnbluOpenTokCallModule and UnbluLiveKitCallModule consist of OpenTok and LiveKit providers. The UnbluCallModule provides the necessary functionality to make audio and video calls.

To create an instance of the UnbluCallModule, call the UnbluCallModuleProvider.create static function. You then register this module via the UnbluClientConfiguration.register function.

The bridge only works if at least one XCFramework WebRTC provider is added to the project: UnbluOpenTokCallModule.xcframework or UnbluLiveKitCallModule.xcframework. You can also add both providers to the project. This allows you to use either of them depending on the Collaboration Server’s settings.

UnbluCallKitModule

This module provides late binding to the iOS CallKit module. If you don’t need the call module, don’t add it to the project. This helps to avoid questions about CallKit in China during the AppStore submission process.

If you’re going to offer VoIP calls, you should add this module to your project along with at least one of the WebRTC providers, UnbluLiveKitCallModule or UnbluOpenTokCallModule.

Even if this module is added, it doesn’t work in China. This is due to restrictions in place on Apple’s part. The restrictions work according to the region specified in the phone’s settings.

VoIP calls still work in China, but instead of the standard iOS call UI, incoming call notifications arrive as regular notifications.

UnbluMobileCoBrowsingModule

The UnbluMobileCoBrowsingModule provides co-browsing functionality which enables real time interaction and collaboration between an agent and a visitor. To create an instance of the UnbluMobileCoBrowsingModule you need to call the UnbluMobileCoBrowsingModuleProvider.create static function, passing in a configuration object of type UnbluMobileCoBrowsingModuleConfiguration. You then register this module via the UnbluClientConfiguration.register function.

UnbluFirebaseNotificationModule

If you use Firebase to receive push notifications from Unblu, you’ll need to import the UnbluFirebaseNotificationModule.

You don’t need to register the UnbluFirebaseNotificationModule.

Using UnbluFirebaseUIApplicationDelegate

As part of the Firebase module, Unblu provides some helper classes to make integration with Firebase easier. One of the classes, UnbluFirebaseUIApplicationDelegate, can be used as the base class for your AppDelegate:

import UnbluFirebaseNotificationModule

class AppDelegate: UnbluFirebaseUIApplicationDelegate {

}

If you choose to subclass UnbluFirebaseUIApplicationDelegate, Unblu will be able to configure Firebase push notifications for you automatically.

To receive push notification data in your AppDelegate, you can override a number of functions:

override func on_application(
    _ application: UIApplication,
    didReceiveRemoteNotification userInfo: [AnyHashable: Any])
{

}

override func on_application(
    _ application: UIApplication,
    didReceiveRemoteNotification userInfo: [AnyHashable: Any],
    fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void)
{

}

To receive the device push token provided by Firebase, you can override the following function:

override func on_messaging(
    didReceiveRegistrationToken fcmToken: String?)
{

}

Manual integration

If you choose not to use UnbluFirebaseUIApplicationDelegate, you need to manually integrate with Firebase and pass relevant push notification data to Unblu, such as device tokens, and remote notification data, when it’s received. To do so:

  1. Enable push notifications in your Xcode project.

  2. Create a VoIP Services certificate in the iOS Developer Center.

  3. Enable the Voice over IP and Remote notifications background modes for your app.

    Firebase integration requirements aren’t listed here.
  4. Initialize the UnbluFirebaseNotificationCoordinator class in the AppDelegate class and assign a reference to an instance of the UnbluFirebaseNotificationCoordinator class to the AppDelegate class variable.

    Listing 1. Example of the AppDelegate class
    class AppDelegate {
    
    var coordinator: UnbluFirebaseNotificationCoordinator?
    ...
    override func application(_ application: UIApplication, didFinishLaunchingWithOptions)
         coordinator = UnbluFirebaseNotificationCoordinator()
    
    }
    ....

    Inside this initialization, an attempt is made to register for push notifications and PushKit notifications. The UnbluFirebaseNotificationCoordinator class also contains a reference to the UnbluNotificationApi class. If you don’t have permission for push notifications, a dialog box is displayed asking for permission.

    UnbluFirebaseNotificationCoordinator
  5. Register to receive push notifications and PushKit notifications.

    If you register to receive PushKit notifications, you need to initialize the UnbluNotificationApi class. A reference to an instance of this class should be stored in a place that isn’t disposed of while the application is running, for example, in a variable in the AppDelegate class. This is because the PKPushRegistryDelegate delegation protocol is provided inside the class. If a reference to it is on the stack, iOS functions can’t access it.

    The code sample below shows how to initialize the UnbluNotificationApi class correctly.

    Listing 2. Example of the UnbluNotificationApi class correctly initialized
    class AppDelegate {
    
    var notificationApi: UnbluNotificationApi?
    ...
    override func application(_ application: UIApplication, didFinishLaunchingWithOptions)
         notificationApi = UnbluNotificationApi.instance
    }
    ....

    This code sample shows an example of what you shouldn’t do.

    Listing 3. Example of the UnbluNotificationApi class incorrectly initialized
    override func application(_ application: UIApplication, didFinishLaunchingWithOptions)
         UnbluNotificationApi.initialize()
  6. Set the device token:

    override func application(_ application: UIApplication, didFinishLaunchingWithOptions)
         notificationApi = UnbluNotificationApi.instance
         notificationApi.deviceToken = <your token>

    For PushKit notifications, the device is registered inside this call:

    	notificationApi = UnbluNotificationApi.instance

    If you’re creating a visitor or agent client, you needn’t initialize the NotificationApi yourself. It’s initialized in the UnbluCoreApi class. You must access the static variable directly:

              UnbluNotificationApi.instance.deviceToken = <your token>

    The instance of the UnbluNotificationApi class, where the token is stored, is created and initialized the first time it’s accessed.

APNs integration

You can use Apple’s Push notification servers without the Firebase middleware. To do so, you must:

  1. Enable this option in the Unblu Collaboratoin Server with the configuration property com.unblu.mobile.push_notification.enableApnsForAllNotifications.

  2. Don’t add UnbluFirebaseModule to your app, and skip all the steps related to this module.

  3. Add the following code to the AppDelegate class and adapt it for your application:

    func application(_ application: UIApplication,
                            didFinishLaunchingWithOptions launchOptions:
                            [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        UIApplication.shared.registerForRemoteNotifications()
    }
    
    func application(_ application: UIApplication,
                        didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        let token = deviceToken.map { String(format: "%02x", $0) }.joined()
        UnbluNotificationApi.instance.deviceToken = token;
    }
    
    func application(_ application: UIApplication,
                        didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
        do {
            try UnbluNotificationApi.instance.handleRemoteNotification(userInfo:
                    userInfo,withCompletionHandler: {_ in
            })
        } catch {
            // If you're here, you've received a notification unrelated to Unblu
        }
    }
    
    func application(_ application: UIApplication,
                        didReceiveRemoteNotification userInfo: [AnyHashable: Any],
                fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
        do {
    
            try UnbluNotificationApi.instance.handleRemoteNotification(userInfo:
                        userInfo,withCompletionHandler: {_ in
            })
        } catch {
            // If you're here, you've received a notification unrelated to Unblu
        }
    }

    As you can see, you must register to receive remote notifications. When you receive a token, assign it to an instance variable deviceToken of the UnbluNotificationApi class. When you receive notifications, pass them to the appropriate methods of the instance of the UnbluNotificationApi class.

    For more information, refer to Registering your app with APNs.

Sample log messages

Some log messages you may encounter when manually integrating with Firebase and passing relevant push notification data to Unblu are listed below with their meaning and origin.

  • pushRegistry(_:didUpdate:for:): <token>

    The device was successfully registered and received the PushKit token.

  • pushRegistry(_:didInvalidatePushTokenFor:for:): <token>

    The device received a command to invalidate the PushKit token.

  • registerPushNotificationToken received shared

    The device was successfully registered and received a secret key for the PushNotificationVersion = Encrypted (origin: client).

  • Prevented emitRegisterPushKitNotificationToken, because api is not initialized. Value: nil

    Either the token wasn’t received and the API was initialized, or the API wasn’t initialized (origin: client).

  • Successfully registered for pushkit notifications

    The device token is received only if it’s a new token and the token isn’t null (origin: client).

  • Successfully registered for remote notifications

    The device token is received only if it’s a new token and the token isn’t null (origin: client).

  • Successfully sent PushKit Notification to device

    PushKit notification was sent to initiate a VoIP call (origin: server).

  • Successfully sent MISSED_CALL Push Notification to device

    A push notification was sent (origin: server).

For more information, contact the Unblu support team.

UnbluView

All Unblu UI content is rendered into a UIView subclass called UnbluView. This is available via the UnbluClient.view property.

The UnbluView provides an Unblu UI that shows all existing conversations. These conversations can be used to chat and make calls.

You need to add the UnbluView to a view hierarchy somewhere in your app for it to be visible on the device. It is up to you to decide the view’s size and position within the context of your application.

You don’t have to add the UnbluView into your app view hierarchy before calling UnbluClient.start, but we recommend that you do so. Unblu will maintain the UnbluView in a hidden view hierarchy until you add it to your own view hierarchy. Likewise, if you remove the UnbluView from your view hierarchy at runtime, Unblu will keep it alive until you call UnbluClient.stop.

Unblu view

Sometimes, Unblu will request that you show the UnbluView via the delegate (see section above), e.g. because there is an incoming call or mobile co-browsing just started. It is up to you to respond to these requests and take the user to wherever you have the UnbluView rendered in your application.

For the hide request, you have to enable the configuration property com.unblu.conversation.ui.autoCollapseIndividualUiOnLayerActivation.

It is also possible to add a "back" button to the Unblu UI that enables the user to explicitly hide the Unblu UI. In this situation, a hide request is sent which you must respond to. The config property that enables this behavior is com.unblu.visitor.ui.showOverviewActionBarCollapseAction on the visitor side, and com.unblu.agent.mobile.ui.showInboxActionBarCollapseAction on the agent side.

If there is an issue with the Unblu UI, please be aware that the UI is rendered inside a WKWebView. You can therefore attach the local Safari to check for error logs. It is a limitation of the iOS WebViews that it is not possible to see the logs inside the normal debugger console.

Conversations

The UnbluConversation protocol provides the neccessary APIs to interact with conversations in Unblu. This includes actions such as opening and closing the conversation, starting audio or video calls, and launching mobile co-browsing sessions.

The functions that start calls and mobile co-browsing sessions require the presence of the UnbluCallModule (and a WebRTC provider) or UnbluMobileCoBrowsingModule, respectively. If the required module is not registered to the Unblu instance, an error will be thrown at runtime.

The current open conversation is always available via the UnbluClient.openConversation property. If no conversation is currently open, the value of the property is nil.

If you wish to be notified when a conversation is opened or closed, adopt the unblu(didChangeOpenConversation openConversation: UnbluConversation?) function in your delegate implementation.

The main functions of note on the UnbluClient relating to conversations are:

Additionally, the UnbluVisitorClient provides:

It is also possible to intercept conversation-related activity by setting the UnbluVisitorClient.conversationInterceptor to an object conforming to the UnbluConversationInterceptor protocol.

Private views

As part of the UnbluMobileCoBrowsingModule, Unblu provides a simple way to add specific views which should not be shown on the remote side. After a view’s tag is added, the view is then automatically overlaid by an image before the screen is transmitted to the remote side. To do so, the UnbluMobileCoBrowsingModuleApi provides the function addPrivateView which takes a tag as its input parameter. You can define a different tag for each view.

You can make a view visible to the remote side again by calling the removePrivateView function, which is also exposed on the UnbluMobileCoBrowsingModuleApi.

Private areas

The SDKs provide a way to specify areas which shouldn’t appear on the remote side. To do so, the UnbluMobileCoBrowsingModuleApi provides the function addPrivateArea which takes a tag as its input parameter. Each private area has its own ID.

After an area is added, it is automatically overlaid by an image before the screen is transmitted to the remote side. You can make an area visible to the remote side again by calling the function removePrivateArea, which is also exposed on the UnbluMobileCoBrowsingModuleApi.

Custom cookies

It is possible to define custom cookies, which should be sent with each request to the Unblu server. This can either be done (initially) by using the UnbluClientConfiguration or by setting the cookies dynamically on your Unblu client instance via the UnbluClient.customCookies property.

When the Unblu client is started, the last cookies configured are always used. If the client is already started, cookies with the same name are overridden, and any new cookies are added. Cookies set here will survive on the Unblu instance even when the client is stopped. They will be applied again to the WebView when the instance is started again. As such, if the client is started again, the last defined cookies are used.

Named areas

Similar to a web page, the named area feature is also available for the mobile SDKs.

A named area can be set either via the UnbluClientConfiguration before starting the client, or via the UnbluClient.namedArea property.

If a named area is set before starting the client, also the specific configuration for the named area is loaded on initialization. If it is set later, it only changes the requests for new conversations in the Agent Desk queue in the way that the agent can filter by the named area. The named area has no effect for existing conversations.

As a consequence, it’s important to know if there will be specific configuration on the named area scope. In this case the named area has to be configured or set in the SDK before you start the client. You should, however, consider changing the configuration at the API key level rather than on the named area. If a named area is only used for the queue, it can be set at any time before starting a new conversation.

File uploads

You can restrict the file types that users may upload with the following configuration properties:

These options affect the Unblu mobile SDK, too. However, the restrictions in place aren’t reflected in the iOS UI. Use com.unblu.filemanager.fileTypeInputTagHint to provide the iOS with a hint as to which file types a user may upload.

Consult the article on configuring file uploads for further information.

Push notifications

If the SDK is configured to use push notifications, there are currently two possible types of notifications visible to the user. Whenever there is a new message and if there is an incoming call. Basically for both types the system default sounds for notifications and calls should be used to provide the best user experience.

If push notifications are disabled, the user’s device token is deleted from the Collaboration Server to ensure they don’t receive any push notifications.

Note that the mobile SDK doesn’t provide users a way to manage their devices. This must be done outside the SDK.

Incoming call notifications

The SDK uses a special type of push notification, PushKit, to trigger an incoming call dialog. PushKit notifications use the Apple Push Notification service (APNs). You must configure the Collaboration Server to send PushKit notifications.

The incoming call dialog is a native iOS user interface. To manage this user interface, we use the CallKit framework. It behaves in the same way that users are accustomed to from other applications.

It isn’t possible to change this UI’s appearance or behavior, except for the icon that appears on the provider call button. You can customize the icon by adding an icon to your application’s resource. The icon must be prepared as described in the Apple documentation. You must also specify the name of the icon in the Unblu client configuration. This is a static variable that you should specify as soon as possible, for example, in the AppDelegate function. If your application is not running and a call is received, the handler that displays the incoming call control user interface will work before initializing the Unblu API.

class AppDelegate {
    ...

    override func application(_:didFinishLaunchingWithOptions:) {

            UnbluClientConfiguration.callKitProviderIconResourceName = "ProviderIcon"

    }

}
Incoming call
Figure 1. iOS incoming call dialog

Voice greeting

Usually, when the iPhone is locked after answering a call, the screen is quickly unlocked using biometric authentication. If the call type is video, the app launches automatically. If there is a delay of a few seconds before the iPhone is unlocked, or if the call type is audio, then after the call is answered, the iOS call control UI is loaded instead of the app being launched. If your app requires authentication before running UnbluView, then the audio stream is not established at this point. In this case, you can set up a voice greeting on the server. Voice greetings are synthesized based on server settings. Thus, after accepting the call, the user hears the voice greeting. For example, the greeting could guide the user to access the call: "Please tap the button to go to the application where a call is waiting for you."

Biometric authentication after receiving a call

If your app uses biometric authentication when the app starts after receiving a call, it is essential to start authentication when the app has an active state. This is because when the incoming call user interface appears, the app starts but is in the background. After the user accepts the call, the app transitions from the background state to the active state and the authentication process can then begin.

App Store review

During the review of your Unblu-enabled iOS app, Apple may request a video recording related to the CallKit and VoIP functions.

The video must show the process of receiving the first call after installing the application. This is because iOS requests permission to access the microphone, camera and so on during the first call. The process of requesting access must be in the recording.

After accepting the call, go to the Home screen to demonstrate the microphone indicators. Finally, return to the call to end it.

The recording must show a physical iOS device, not a simulator.

Encrypted notifications

Push notifications may or may not be encrypted:

var config = UnbluClientConfiguration(...)
config.unbluPushNotificationVersion = .Encrypted (1)
1 See below for the other versions available.

The SDK provides three versions of push notification:

  • Encrypted: All notifications are encrypted. The SDK decrypts the notifications automatically.

  • EncryptedService: All notifications are encrypted. The notifications are decrypted in the notification service extension. You must add the notification service extension yourself.

  • None: Notifications aren’t encrypted. This is a legacy option.

  • Encrypted: all notifications are encrypted, decryption automatically goes inside the SDK

  • EncryptedService: all notifications are encypted, decryption goes in the Notification service extension (has to be added manualy).

Notifications are decrypted with a key stored in Keychain Access. This key is automatically updated from the Collaboration Server over a protected HTTPS connection.

Notification service extension

This is a special module that should be manualy added to the Application that modifies the content of a remote notification before it is delivered to the user.

  1. First, follow the instructions in the Apple Developer Documentation.

  2. To save the secret key and access it from the extension, add the keychain group group.com.unblu.coreSdk.shared to the Keychain Sharing section for the app and the extension.

  3. Add UnbluCoreSDK.framework (Do not Embed) to Framework and Libraries.

  4. Configure the Unblu SDK to use the service extension:

    var config = UnbluClientConfiguration(...)
    config.unbluPushNotificationVersion = .EncryptedService
  5. Replace the code in the NotificationService class with the code below:

    class NotificationService: UNNotificationServiceExtension {
    
        var contentHandler: ((UNNotificationContent) -> Void)?
        var bestAttemptContent: UNMutableNotificationContent?
    
        override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
    
            self.contentHandler = contentHandler
            bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
            if let bestAttemptContent = bestAttemptContent {
                if  let dictonary = decryptBody(userInfo: bestAttemptContent.userInfo)
                {
                    guard let body =  dictonary["text"] as? String else {
                        contentHandler(bestAttemptContent)
                        return
                    }
                    guard let title = dictonary["title"] as? String  else {
                        contentHandler(bestAttemptContent)
                        return
                    }
                    bestAttemptContent.userInfo = dictonary as [AnyHashable : Any]
                    bestAttemptContent.body = body + " [via NotificationService] "
    
                    bestAttemptContent.title = title
                    bestAttemptContent.sound = .default
    
                } else {
                    bestAttemptContent.body = ""
                    bestAttemptContent.title = ""
                }
                contentHandler(bestAttemptContent)
            }
        }
    
        func decryptBody(userInfo: [AnyHashable : Any]) -> [String : Any?]? {
            if let encryptedData = userInfo[UnbluEncryptedNotificationServiceHelper.KEY_ENCRYPTED_DATA] {
                guard let dictonary = UnbluEncryptedNotificationServiceHelper.decode(encryptedData as! String)  else {
                    return nil
                }
                return dictonary
            }
            return nil
        }
    
        override func serviceExtensionTimeWillExpire() {
            // Called just before the extension will be terminated by the system.
            // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
            if let contentHandler = contentHandler, let bestAttemptContent =  bestAttemptContent {
                contentHandler(bestAttemptContent)
            }
        }
    
    }

Logging

The Unblu SDK uses an internal Logger, which can be configured. By default, the logger doesn’t print any additional, potentially security related information. To change this behavior for debugging purposes, set the flag UnbluClient.enableDebugOutput to true. You can change the log level using the field UnbluClient.logLevel.

It is also possible to enable performance logging for mobile co-browsing. This is done via the UnbluMobileCoBrowsingModuleConfiguration object used to configure the UnbluMobileCoBrowsingModule. For performance logging, the log level has to be .debug or lower, and the UnbluClient.enableDebugOutput flag must be enabled.

Security-relevant configuration

There are several security-related configuration possibilities when integrating the Unblu iOS SDKs. The following points should be taken into consideration to ensure the SDK is configured in the most secure way without limiting the features available.

Whitelist URLs

By default, only the configured Unblu base URL and its subroutes may be accessed by the SDK’s internal WebView. On the other hand, you must be able to specify that a URL is also accessible if, say, the initial URL is redirected to another domain. To this end, you can provide a list of regular expressions by way of the UnbluClientConfiguration.internalUrlPatternWhitelist variable. If you do not have this requirement, there is no need to configure the internal pattern whitelist as it is secure per default.

The patterns in the internal pattern whitelist are those used by the Unblu-enabled application itself. If you want to specify URL patterns which may be accessed by having your application launch another application, modify the external link pattern whitelist described in the section below. This applies even if the patterns describe URLs within your organization’s domain.

If you want to use Google fonts in your mobile app, take into account that https://fonts.googleapis.com redirects to https://fonts.gstatics.com. You will have to include a pattern for the latter domain in the whitelist.

Whitelisting external links is handled by the externalLinkHandler property and the UnbluExternalLinkHandler protocol.

When you initialize UnbluClientConfiguration, you must provide an object that conforms to the UnbluExternalLinkHandler protocol. Unblu provides a default implementation of this protocol called UnbluDefaultExternalLinkHandler.

When a link is tapped in a conversation, Unblu calls the function decidePolicy(for: URL) → UnbluExternalLinkHandlingPolicy on the UnbluExternalLinkHandler. How Unblu proceeds depends on whether the function returns .open, .block, or .ignore. This check occurs before the request is sent to the Collaboration Server. Iframes are also checked against the whitelist.

UnbluClientConfiguration exposes the link handler via the new externalLinkHandler property.

For more information, please review the Unblu iOS mobile SDK reference entries of the following types:

Certificate pinning

The SDK uses a WKWebView for most of it’s communication, and URLSession for file downloads. For certificate pinning the WKNavigationDelegate method is called to handle URLAthenticationChallanges and file download challenges triggered via URLSessionDelegate method.

To define a handler for these challenges, you need to provide an object that conforms to the AuthenticationChallengeDelegate protocol and pass it to UnbluClientConfiguration.authenticationChallengeDelegate.

UnbluAuthenticationChallengeHandler

If you do not want to write the code to handle certificate pinning yourself, Unblu provides a class that does the heavy lifting for you, and all you have to do is pass the certificate data, or public keys. This class is called UnbluAuthenticationChallengeHandler. Please refer to the documentation for this class for more information on how to use it.

File downloads

The SDK needs to know how to download files.

In most cases, the default handling provided by UnbluDefaultFileDownloadHandler is sufficient, so passing an instance of this class to the initializer of UnbluClientConfiguration is all you’ll need to do. However, if you want to implement your own solution, you must pass your own object conforming to the UnbluFileDownloadHandler protocol to the initializer of UnbluClientConfiguration.

Log stripping

In iOS, there’s no need to explicitly strip out log statements as they can’t be accessed from the binary of the app by simple means anymore, although they are visible within the binary files delivered by Unblu.

Encrypting stored preferences

The SDK needs to store some internal values inside the app to properly identify the user device against the Collaboration Server even after restarting the app. By default, UserDefaults.standard is used to store these values. this is achieved by passing an instance of UserDefaultsPreferencesStorage to UnbluClientConfiguration when it is initialized.

In many cases this is enough because normally, only the app itself has access to this data. You may, however, wish to encrypt the values before storing them. To do so, you have two options:

  • Provide the SDK with a custom implementation of UnbluPreferencesStorage when you initialize UnbluClientConfiguration. The SDK will then use this storage whenever it needs to write or read the value of a preference to persistent storage.

  • Use UnbluKeychainPreferencesStorage to store preferences in the iOS keychain. Refer to the documentation for this class for more information on how to use it.

Working with keyboards

If your app uses SwiftUI, the height of the container to which the UnbluView is added is automatically changed to avoid keyboard overlap.

If you use UIKit, you must ensure the height of the container is changed by adding observers to the events keyboardWillShowNotification and keyboardWillHideNotification, where the height should be adjusted according to the height of the keyboard.

Error handling

You can get an UnbluClientErrorType error while initializing the SDK in the start(_:) function’s completion handler or after a successful initialization in the UnbluClientDelegate delegation protocol.

Most types of error are fatal. This means that, in most cases, the main SDK functions no longer work properly. When an error occurs, you should prevent users from accessing the Unblu UI and inform them that the Unblu functionality is momentarily unavailable.

There are also non-fatal errors:

If one of the above errors occurs (see Authorization above), you can pass a valid token, for example by verifying that the user is logged in. Later, you can try to restart the API if the error occurred on startup. If it’s a runtime error received in UnbluClientDelegate, continue as usual.

Weak variables

The variables listed below are weak. The instance assigned to these variables must already be referenced prior to being assigned to them.

  • delegate in UnbluMobileCoBrowsingModuleApi

  • delegate in UnbluCallModuleApi

  • delegate in UnbluFirebaseNotificationCoordinator

  • visitorDelegate in UnbluVisitorClient

  • pagentDelegate in UnbluAgentClient

Known issues

  • UIDocumentInteractionController isn’t compatible with the Unblu mobile co-browsing engine. If you are using UIDocumentInteractionController to display a PDF and you want this PDF to be captured by Unblu during mobile co browsing, either display your PDF using WKWebView or Apple’s PDFKit framework.

  • SFSafariViewController isn’t compatible with the Unblu mobile co-browsing engine.