Getting Started - Bandyer/Bandyer-iOS-SDK GitHub Wiki

This guide will show you how to start integrating the Kaleyra Video iOS SDK 3.x versions. If you are looking for the 2.x versions guide take a look at this guide instead

Getting started

This guide will show you the basic steps you need to make in order to integrate the Kaleyra Video in your iOS app. From now on we are assuming you already have obtained your Bandyer credentials.

Table of contents

SDK configuration

The first thing we need to do in order to use the Kaleyra Video iOS SDK is configuring and initializing it. In the code samples below, we are going to initialize the Kaleyra Video iOS SDK in our sample AppDelegate. We do it there for convenience, but you are not required to do so, you can initialize the Kaleyra Video iOS SDK in anywhere you want in your code, just make sure to do it only once. You must create a configuration object the SDK will read to configure itself during initialization, you create such object using the builders provided by the SDK. The SDK provides one builder for Swift code and one for Objective-c code. You are required to provide three parameters to the builder:

  • The appID identifying your company in the Kaleyra Video platform (beware this identifier is bound to a particular environment and region, if you choose the wrong combination you might not be able to connect your client)
  • The environment you want your client to connect to
  • The region you want your client to connect to.

Let's pretend we want to configure the SDK for the sandbox environment, in europe region, enabling CallKit, automatic VoIP notifications handling and enable the following tools: chat, file share, in-app screen sharing and the whiteboard.

In Swift you'd do like this:

import UIKit
import Bandyer

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        let config = try! ConfigBuilder(appID: "APP ID", environment: .sandbox, region: .europe)
                                .callKit { callkit in
                                    callkit.enabled { provider in
                                        provider.supportedHandles([.generic])
                                            .icon(try! UIImage(named: "callkit-icon"))
                                    }
                                }
                                .tools { tools in
                                    tools.chat()
                                         .fileshare()
                                         .whiteboard(uploadEnabled: true)
                                         .inAppScreenSharing()
                                }
                                .voip { voip in
                                    voip.automatic(pushRegistryDelegate: self)
                                }
                                .build()

        BandyerSDK.instance.configure(config)
        
        return true
    }
}

In Objective-c you'd do like this:

#import <Bandyer/Bandyer.h>

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    BDKConfig *config = BDKConfigBuilder.create(@"My APP id", BDKEnvironmentSandbox, BDKRegionEurope)
        .callKit(^(BDKCallKitConfigurationBuilder * callkit){
            callkit.enabledWithConfiguration(^(BDKCallKitProviderConfigurationBuilder * provider) {
                provider
                    .supportedHandles(@[@(CXHandleTypeGeneric)])
                    .iconImage([UIImage imageNamed:@"callkit-icon"]);
            });
        }).voip(^(BDKVoIPPushConfigurationBuilder * voip){
            voip.automaticBackground(self, nil);
        }).tools(^(BDKToolsConfigurationBuilder *tools){
            tools.chat()
                 .fileshare()
                 .inAppScreensharing()
                 .whiteboard();
        }).build();


    [BandyerSDK.instance configure:config];

    return YES;
}

@end

As you might have noticed, once we set all the flags and configuration needed on the configuration object, we told the SDK to configure itself providing it the configuration object created previously. This step, sets up the SDK internally, but you won't start receiving incoming calls nor you can make outgoing calls yet. You must connect the SDK before you can do anything.

Connecting the SDK

The next step you must perform in order for the SDK to start processing incoming calls and accept outgoing call is connect it providing a session object. The Session object represents the session of the user identified by the identifier you provide in its initialiser. You create a Session object providing both an user identifier and an AccessTokenProvider a component the SDK will use to retrieve access tokens for the user when needed.
Once your app user has been authenticated you should connect the SDK calling the connect(_:completion:) method on the BandyerSDK singleton instance providing a session object.

Let's pretend we are writing an app that has some kind of authentication mechanism. After we have authenticated our user (either she/he has provided hers/his authentication credential on a login screen or we resumed her/his user session) we show her/him our wonderful app main screen. There we are going to connect the Kaleyra Video SDK.

func onUserAuthenticated(_ userAlias: String) {
    let session = Session(userId: "alice", tokenProvider: TokenProvider())
    BandyerSDK.instance.connect(session) { error in }
}
- (void)onUserAuthenticated:(NSString *)userAlias
{
    BDKSession *session = [[BDKSession alloc] initWithUserId:@"alice" tokenProvider:[TokenProvider new] observer:nil];
    [BandyerSDK.instance connect:session completion:^(NSError * error) {

    }];
}

When the user signs off don't forget to call the disconnect() method on the BandyerSDK singleton instance. That method will stop any running client and it will close the user session.

Access token provider

Starting from 3.0 version the Kaleyra Video SDK uses a strong authentication mechanism based on JWT access tokens. Anytime the SDK needs to authenticate a user or refresh the user session it will call the provideAccessToken(userId:completion:) method on the AccessTokenProvider instance provided to the current Session object asking a for an access token (the provideAccessTokenWithUserId:success:error method is going to be called if you are using Objective-c instead). It is up to you how to retrieve the access token, however you must invoke the completion closure provided as second argument notifying whether the operation has succeeded or failed with a result value (in Objective-c you must call one of the two blocks provided as arguments). Access tokens are generated on demand by our back-end system through a REST api, please take a look at our REST api documentation for more information.

In the code snippets below you'll find an example of an AccessTokenProvider retreving the access token from a "fake" back-end through a REST call:

import Foundation
import Bandyer

class RestAccessTokenProvider: AccessTokenProvider {

    private let client: HTTPClient
    
    private lazy var decoder: JSONDecoder = {
        let decoder = JSONDecoder()
        decoder.dateDecodingStrategy =  .formatted(DateFormatter.remoteApiFormatter)
        return decoder
    }()

    init(client: HTTPClient) {
        self.client = client
    }

    func provideAccessToken(userId: String, completion: @escaping (Result<String, Error>) -> Void) {
        let request = URLRequest(url: URL(string: "https://my-app.com/user/access_token?user_id=\(userId)")!)

        client.post(request) { (result) in
            completion(Result {
                switch result {
                    case let .success(result):
                        do {
                            if (200..<300).contains(result.response.statusCode) {
                                return try decoder.decode(String.self, from: result.data)
                            } else {
                                throw BadRequestError()
                            }
                        } catch let(error) {
                            throw error
                        }
                    case let .failure(error):
                        throw error
                }
            })
        }
    }
}

@interface RestAccessTokenProvider : NSObject <BDKAccessTokenProvider>

@property (nonatomic, strong, readonly) HTTPClient *client;

@end

@implementation RestAccessTokenProvider

- (void)provideAccessTokenWithUserId:(NSString * _Nonnull)userId
                             success:(void (^_Nonnull)(NSString * _Nonnull))success
                               error:(void (^_Nonnull)(NSError * _Nonnull))failure 
{
    NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"https://my-app.com/user/access_token?user_id=%@", userId]];
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];

    [self.client post:request completion:^(NSString *accessToken, NSError *error) {
        if (error != nil)
        {
            failure(error);
        }else
        {
            success(accessToken);
        }
    }];
}

@end

Where to go from here

Now that you have a basic implementation that can connect to the Kaleyra Video platform you are ready to start making outgoing calls or receive incoming calls. We also recommend you to take a look at the call client lifecycle guide that will explain you how to respond to the call client state change events.

What's next