Introduction

This document serves as an overview for developers intending to integrate with Rebtel SDK for the first time. Furthermore, it outlines the prerequisites and guides the developer through the process of setting up and answering calls as well as sending and receiving instant messages.

The Rebtel SDK is a communication library that makes adding voice and/or messaging to mobile apps very easy. It handles all the complexity of signaling and audio management while providing the app developer the freedom to create a stunning UI.

The reference documentation contains a comprehensive description of all the classes. Please refer to the links below to find the information for your target platform:

The SDK archive can be downloaded here. It contains: The library binary, this user guide, the reference documentation and sample apps for calling and instant messaging.

A Graphical Overview

Figure 1. Schematic view over basic calling setup.
Figure 1. Schematic view over basic calling setup.

The image shows a high-level architecture overview. The two users are connected through the Rebtel cloud and talk with each other.

Glossary

The glossary below defines some of the domain specific terms used throughout this document.

Term Explanation
Caller (or “A”) The user initiating a call.
Callee (or “B”) The user receiving a call.
Application The mobile application running on iOS and/or Android. A partner can have more than one application.
Application Instance One installation of the application on a single device.
Application Key A key generated by Rebtel. The key is unique to the application. A key looks like 196087a1-e815-4bc4-8984-60d8d8a43f1d (lowercase hexadecimal formatted GUID).
Application Secret A string generated by Rebtel. The secret is used to verify the application. A secret looks like oYdgGRXoxEuJhGDY2KQ/HQ== (Base64-encoded string representation).
User A user of the mobile application. The actual person holding the mobile device.
User Identity Identity of a user in the application domain. Can be any string, for instance a user name, user id, phone number or email address.
PSTN The public switched telephone network, i.e. the regular telephone network.
Active Connection A socket connection for signaling purposes where incoming calls are received.

First Time Setup

There are a few steps that need to be completed the first time the Rebtel SDK is used.

First, the application needs to be registered with Rebtel. Registration is available through the Rebtel Developer web at http://developer.rebtel.com/register where you will be able to obtain an Application Key and an Application Secret.

Minimum Requirements

iOS 5.0 is the minimum iOS version required for using the Rebtel SDK (iOS Deployment Target).

Adding the Rebtel Framework

Add the Rebtel SDK for iOS Framework by dragging the RebtelSDK.framework folder from the SDK distribution package folder into the Frameworks section of the Project Navigator.

The Rebtel SDK depends on the following frameworks, which must be added to the project and linked with the application target.

  • AudioToolbox.framework
  • AVFoundation.framework
  • Security.framework
  • libsqlite3.dylib

Some additional linker flags need to be added. In the Build Settings pane for the application target, set the following:

  • Other Linker Flags -> -ObjC -Xlinker -lstdc++

Note: The -Xlinker flag is especially important since iOS 7.0 and Xcode 5.

In the Info.plist, add

  • Required background modes (UIBackgroundModes):
    • Application plays audio (audio)
    • Application provides Voice over IP services (voip)

Note: The Rebtel SDK library uses Automatic Reference Counting (ARC). However, it can still be used in non-ARC projects.

RebtelSDK is available as a CocoaPod

If you are using CocoaPods, RebtelSDK for iOS is available as a CocoaPod. Add the following to your Podfile:

pod 'RebtelSDK'

Note on RebtelSDK.framework file size vs linked size

The RebtelSDK.framework has a fairly large file size (65 Mb). This is not the additional size that will be added to your application when linking against the RebtelSDK.framework. The static library within RebtelSDK.framework includes Debug symbols for the purpose of making it possible to symbolicated names for classes and methods defined in the RebtelSDK, to ease the process of analyzing crash logs. When building your application for Release, the debug symbols will be stripped from the actually application binary and packed into a dSYM-file, thus reducing the size of the runnable binary.

Note: The size that the RebtelSDK adds to a Release application binary is around 8.1 Mb (calculated based on armv7 and armv7s)

Live and Test Environment

Rebtel provides two environments:

  • Live environment - Should be used for applications deployed in production.
  • Sandbox environment - Should be used during development and testing.

The environment is passed as a parameter to the client instantiation method (see example above).

When registering an application in the developer account, two sets of Application Key and Application Secret will be generated, one for the Live environment and one for Sandbox.

Environment EnvironmentHost parameter
Live clientapi.rebtel.com
Sandbox sdksandbox.rebtel.com

Restrictions on User IDs

User IDs are restricted to only contain characters in the printable ASCII character set. That is:

!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~

User IDs must not be longer than 64 characters.

Rebtel Client

Starting the Client

To make calls or receive incoming calls an instance of the Rebtel Client is needed. To create it, the following information is needed:

  • Application Key
  • Application Secret
  • Environment Host
  • User ID

The Application Key and Application Secret are obtained from Rebtel. For Environment, choose either sandbox or live (see the section Live and Test Environment). The User ID uniquely identifies the user running the Rebtel Client.

Then, set up the client delegate (see Reference documentation).

Lastly, the Rebtel client must be started, and directed to start listening for incoming calls. This is accomplished by calling the methods start and startListeningOnActiveConnection. The method start will initiate the internals of the Rebtel client, and if it’s the first time it’s starting with a specific User ID, the client will register that user identity against the Rebtel service.

Setting up the client:

#include "RebtelSDK.h"

// Instantiate a client object using the client factory method.
id<REBClient> client = [RebtelSDK clientWithApplicationKey:@"<application key>" 
                                         applicationSecret:@"<application secret>"
                                           environmentHost:@"sdksandbox.rebtel.com" 
                                                    userId:@"<user id>"];

// Specify additional client capabilities (optional)
[client setSupportActiveConnectionInBackground:YES]; // (optional)
[client setSupportPushNotifications:YES]; // (optional)                                                       

client.delegate = ...;

// Start the Rebtel Client
[client start];

// Start listening for incoming calls on the active connection.
[client startListeningOnActiveConnection];

Note: If the application is meant to only make outgoing calls but not receive incoming calls, simply don’t call the startListeningOnActiveConnection. Outgoing calls can be made after calling the start method, and after the delegate has received the callback clientDidStart:.

For applications that want to be able to receive incoming calls while not running in the foreground, a couple of additional steps are required. Refer to the Background Mode and [Offline Calls][] sections for details.

Life Cycle Management of a REBClient-instance

It is generally recommended to initiate the Rebtel client, start it, but not stop it, during the lifetime of the running application. That also implies that the REBClient-instance should be retained by the application code.

If incoming calls are not desired, it is recommended to only stop listening for incoming calls by invoking -[REBClient stopListeningOnActiveConnection]), but not invoking -[REBClient stop]. This is simply because initializing and starting the client is relatively resource-intensive in terms of CPU. Therefore it is best to keep the client instance alive and started unless deemed necessary for reasons specific to your application. It should not even be necessary to dispose of it if memory warnings are received from iOS, because once the client is started it does not use very much memory in comparison to view layers, view controllers etc.

For the same reason, if the support for push notifications is enabled, the preferred method of temporarily stopping incoming calls via push notifications is to unregister the push notification data (see [Offline Calls][]).

The Rebtel client can of course be completely stopped and also disposed. If desired, it is of importance to first explicitly call the stop method on the client before the application code releases its last reference to the client object.

Example of how to dispose the Rebtel client:

[client stopListeningOnActiveConnection];   
[client stop];
[client release]; // unless using ARC
client = nil;

Calling

Setting up a Call

Setting up a call using the SDK is easy. Start the Rebtel client (described in Starting the Client). Then use the client to start the call via the method callUserWithId:, by passing the user identifier of the callee as argument.

id<REBCall> call = [client callUserWithId:@"<remote user id>"];

A call object will be returned, containing details about the participants in the call, call details such as start time, call state, possible errors etc.

Assuming the callee’s device is available and responsive, the delegate method callReceivedOnRemoteEnd: will be called. It notifies the application that the other side has received the incoming call request. If a progress tone should be played, this is where it should be started. It is strongly recommended that the available functionality, provided by the Rebtel SDK, to play sounds such as ringtones is used for this purpose. See Playing Ringtones for details.

When the other party answers, the callEstablished: call delegate method will be called. At this point, the users can start talking. If a progress tone has been initiated, it should be stopped now, in the delegate callback method.

Answering a Call

To be able to answer calls, the application has to be notified when the user receives an incoming call.

Start the Rebtel client (described in Starting the Client). The client delegate will then be notified via the delegate method didReceiveIncomingCall: as the calls come in to the device.

When the delegate method is executed, the call can either be connected automatically without any user action, or it can wait for the user to press an answer button. It is also recommended that ringtones are played from within the delegate callback method. See Playing Ringtones for details.

Answer an incoming call:

- (void)client:(id<REBClient>)client didReceiveIncomingCall:(id<REBCall>)call {
    call.delegate = self;

    // Connect the call straight away
    [call answer];
}

Set the call delegate in order to get events related to the call.

Connect the call by using the answer method on the call object passed to the method. The call object contains details about participants, start time, potential error codes and error messages.

At this point, the clients on both ends will establish the connection. When the call is established and the voice streams are running in both directions, the callDidEstablish: delegate method will be called. Stop the ringtone and allow for the conversation to start.

If the call should not be answered, simply use the hangup method on the call to decline. The caller will be notified that the incoming call was denied.

Hanging up an incoming call:

-  (void)client:(id<REBClient>)client didReceiveIncomingCall:(id<REBCall>)call {
    call.delegate = self;

    // User does not want to answer
    [call hangup];
}

Disconnecting a Call

When the user wants to disconnect an ongoing call, use the hangup method. Either user taking part in a call can disconnect it.

Hanging up a call:

[call hangup];

When either part disconnects a call, the application will be notified via the call delegate method callEnded:. This allows for the user interface to be updated, an alert tone to be played, or similar.

A call can be disconnected before it has been completely established.

Hanging up a connecting call:

// Starting a call
id<REBCall> call = [client callUserWithId:@"<remote user id>"];

// User changed his/her mind, let’s hangup
[call hangup];

Handling a call that ends:

// REBCallDelegate implementation

- (void)callEnded:(id<REBCall>) call {
  // update user interface, e.g. hide the call screen.
}

Audio Handling

If the application plays audio that does not originate from the Rebtel SDK, certain guidelines should be followed. Additional information on audio session related topics is available in the Audio Session Programming Guide.

Audio Sessions

When a call is established, the Rebtel client will activate the shared audio session. When the call is later disconnected, the Rebtel client will deactivate the shared audio session. This means, if the application wants to play audio in any other context, it needs to reactivate the shared audio session. This applies after each finished call.

The Rebtel SDK client will, when started, set itself as the audio session delegate. If the application wishes to override this behavior, the application needs to reset the delegate after the client has started but before any call has been started. (Note that this is not recommended.)

Audio Session Categories

When the Rebtel client is started, it will set the audio session category to PlayAndRecord. The main reason the Rebtel client only set the audio category once, when started, is to avoid interfering with what the hosting application may want to be doing with the audio session. Though this also means that if the application changes category, it is responsible for changing the category back to PlayAndRecord after the application has performed its audio task, so that the category is correctly setup for calls.

The Rebtel SDK will apply the audio session category mode AVAudioSessionModeVoiceChat for improved voice quality. Please see Apple’s AVAudioSession documentation for further details.

Audio Session Interruptions

When the users are in the midst of a Rebtel SDK call, someone might call either users via the PSTN network, thus interrupting the application and make iOS play the regular native ringtone. If the native phone call ends within 30 seconds, the application will start running again. If not, the Rebtel SDK call will be terminated.

Playing Ringtones

The REBAudioController object provides a convenience method startPlayingSoundFile:loop: for playing sounds that are related to a call, such as ringtones and busy tones. Details on how to use it can be found in the Reference documentation.

The sound file must be a mono (1 channel), 16-bit, uncompressed (PCM) .wav file with a sample rate of 8kHz, 16kHz, or 32kHz.

- (void)callReceivedOnRemoteEnd:(id<REBCall>)call {
    NSString* soundFilePath = [[NSBundle mainBundle] pathForResource:@"progresstone" ofType:@"wav"];
    // get audio controller from REBClient
    id<REBAudioController> audioController = [self.client audioController];
    [audioController startPlayingSoundFile:soundFilePath loop:NO];
}

Applications that prefer to use their own code for playing sounds are free to do so, but they should follow the guidelines for Audio Sessions and Audio Session Categories above.

Background Mode

Normally, when an iOS app enters background mode, all its sockets are closed by the OS. For VoIP apps, iOS provides the option to keep a signaling socket open in background mode, by tagging it as a VoIP socket. The Rebtel SDK utilizes this option to be able to receive incoming calls while the app is in the background.

If the app is not running at all, either in the foreground or the background, the app is considered to be offline. In this case the Rebtel SDK will optionally resort to using push notifications to notify the app of incoming calls. Which push notification service to use is up to the application developer, but for iOS devices, the typical choice would be to use Apple Remote Push Notifications. Refer to the [Offline Calls][] section for details.

To enable the use of a VoIP tagged signaling socket for receiving incoming calls while in background mode, call the setSupportActiveConnectionInBackground: method on the Rebtel client object:

[client setSupportActiveConnectionInBackground:YES];

Note that the setSupportActiveConnectionInBackground: method should be called before the client has been started with the start method.

When an incoming call is received, iOS will notice that there is pending activity on the VoIP signaling socket and it will temporarily wake up the app. The app will not be entirely awake, but it will get a few seconds to let the Rebtel SDK process the received data in the background. If a call was received, the Rebtel SDK will schedule a local iOS notification, which will be presented to the user immediately.

If the user taps the notification, iOS will bring the app back into the foreground. The notification object contains information that is needed by the Rebtel SDK to start the incoming call. To hand over the notification object to the Rebtel client, use the relayLocalNotification: method:

- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {

    if ([notification reb_isRebtelNotification]){

      // this will trigger -[REBClientDelegate didReceiveIncomingCall:] if the notification
      // represents a call (i.e. contrast to that it may represent an instant-message)
      id<REBNotificationResult> result = [client relayLocalNotification:notification];

      if (result.isCall && result.callResult.isTimedOut) {
        // The notification is is related to an incoming call,
        // but was too old and the call has expired.
      }
    }
}

Customizing the Local Notification Content

Apps that want to be able to receive calls with a VoIP tagged socket in background mode must implement the delegate method client:localNotificationForIncomingCall:. The delegate is responsible for composing a REBLocalNotification object with content that will be shown to the user when the local notification is presented to the user by the OS. The content includes various descriptive strings, a badge number, and the path to a sound file that will be played when the notification is presented.

Answering when Notified

Once the Rebtel SDK has processed the call information extracted from the notification, it will call the delegate method client:didReceiveIncomingCall:. Depending on the desired behavior for the app, the incoming call may either be treated as any other incoming call and let the user tap an additional button to answer it, or the call may be answered automatically when the user has acted on the local notification.

If it’s the latter case, the app can determine whether the call originated from background mode or not by examining the applicationStateWhenReceived property of the call details object. If the application was active when it received the call, it means the app was in the foreground. This approach is also applicable to offline calls with Apple Push Notifications.

- (void)client:(id<REBClient>)client didReceiveIncomingCall:(id<REBCall>)call {
    call.delegate = self;

    if (call.details.applicationStateWhenReceived == UIApplicationStateActive) {
        // Show an answer button or similar in the UI
    } else {
        // application was in not in the foreground when the call was initially received,
        // and the user has opened the application (e.g. via a Local Notification),
        // which we then interprete as that the user want to answer the call.
        [call answer];
    }
}

Rebtel Local Notifications and the Notification Center

If the Rebtel SDK is used with support for an active signaling connection in the background enabled (it is disabled by default), local notifications will be used. The following is good to know about how the Rebtel SDK handles local notifications and how it affects what is presented in the Notification Center.

  • A notification passed in to the method -[REBClient relayLocalNotification:notification:] will be removed from Notification Center after it has been handled.
  • A local notification representing an incoming call will be removed if the call times out.
  • Invoking -[REBCall hangup] from -[REBClient client:localNotificationForIncomingCall] is a valid operation and can be used to dismiss a call while the using is busy talking in the regular phone app. This will effectively prevent the SDK from invoking the -[REBClientDelegate client:didReceiveIncomingCall:] method when the app returns to foreground.
  • Invoking -[REBCall answer] while being in the background is possible. The call will not immediately be answered but the operation will be pended and the call answered once the app returns to the foreground.

Instant Messaging

The REBMessageClient is the entry point to Instant Messaging functionality in the Rebtel SDK.

Messages are sent through the REBMessageClient and events are received via the REBMessageClientDelegate. The message client is owned by the REBClient and accessed via -[REBClient messageClient]. Instant messaging is not enabled by default. In order to enabled instant messaging, - [REBClient setSupportMessaging: YES] must be set.

REBClient rebtelClient;
[rebtelClient setSupportMessaging: YES];
REBMessageClient messageClient = [rebtelClient messageClient];

// Assign a delegate for instant messages events
messageClient.delegate = ...

Sending a Message

Messages are created using the static method [REBMessage messageWithRecipient:text]. Once created sending the message is as simple as calling -[REBMessageClient sendMessage:]:

REBOutgoingMessage *message = [REBOutgoingMessage messageWithRecipient:@"<recipient user id> text:@"Hi there!"];

[messageClient sendMessage:message];

Message delivery success

When a message is successfully sent, the delegate will be notified:

// REBMessageClientDelegate

- (void) messageSent:(id<REBMessage>)message {
  // Persist outgoing message
  // Update UI
}

Updating the UI from the messageSent: callback is especially convenient when a user is logged in into more than one device simultaneously. The messageSent: callback will be fired on each device. This aids in keeping the UI consistent across devices.

As soon as the system has confirmed the messages were delivered the delegate will be notified using the messageDelivered:method. Inspecting the infoparameter passed to the callback reveals more details on the specific event.

- (void) messageDelivered:(id<REBMessageDeliveryInfo>)info {
    NSLog(@"The message with id %@ was successfully delivered at server time %@", 
    info.messageID, info.timestamp);
}

Message delivery failures

Delivering a message can fail for various reasons: there might not be a network available, the recipient does not have instant messaging support etc. When a message failed to reach its destination the delegate is notified via the messageDeliveryFailed: callback. The reason for failing to deliver a message is propagated back as an array of REBMessageFailureInfo instances.

- (void) messageDeliveryFailed:(id<REBMessage>) message info:(NSArray *)messageFailureInfo {
    for (id<REBMessageFailureInfo> reason in messageFailureInfo) {
        NSLog(@"Delivering message with id %@ failed to user %@. Reason %@", 
        reason.messageId, reason.recipientId, [reason.error localizedDescription]);
    }
}

Note: Messages are stored in the backend for 30 days before being removed. If the recipient has not started the app and downloaded the message history within this time frame, the message will be lost and no notification received.

Resending a failed message

When a message has been returned in messageDeliveryFailed: the message instance is considered stale and should not be resent. Resending a failed message will result in an undefined behavior. Instead a new message should be created using -[REBOutgoingMessage messageWithMessage:]. This will create a new message with the same contents as the original message but with a new internal state that can be sent.

Receiving a Message

Incoming messages will be delivered to the delegate:

- (void) messageClient:(id<REBMessageClient>) messageClient 
          didReceiveIncomingMessage:(id<REBMessage>)message {             

  // Present a Local Notification if app is in background
  if([UIApplication sharedApplication].applicationState == UIApplicationStateBackground){

    UILocalNotification* notification = [[UILocalNotification alloc] init];
    notification.alertBody = [NSString stringWithFormat:@"Message from %@",
                                                        [message recipientIds][0]];

    [[UIApplication sharedApplication] presentLocalNotificationNow:notification];
  } else {
    // Update UI in-app
  }

  // Persist incoming message

}

Note: For instant-messages, managing iOS Local Notifications is completely up to the application (in contrast to the case of incoming calls where the Rebtel SDK manages the local notifications).

Push notifications

An application is considered offline in the following scenarios:

  • When the application is not running at all.
  • When background mode has been disabled for the Rebtel client, and the application is not in the foreground.

For these two scenarios, offline support must have been implemented for the application to still be able to receive incoming calls and instant messages. The following sections cover how to support receiving offline calls and messages and how to handle them.

The Rebtel client relies on a push service to launch the application if it is not currently listening for incoming calls or messages due to the application being offline. Which push service to use is up to the developer, but for iOS applications, the typical choice would be to use Apple Remote Push Notifications.

When offline, the recipient of a call or message will receive a push notification containing a Rebtel-specific payload which enables the Rebtel client to connect the incoming call or message. Acting on the push notification brings the application to the foreground to allow the user to answer the call or view the message.

Figure 2. Push notification sequence.
Figure 2. Push notification sequence.

Figure 2 describes the following sequence of events: Both users start their applications and Rebtel clients. B kills the application, and A calls B. Rebtel notices that B is offline, and tells A to send a push notification to B so that B can answer the call.

When the Rebtel client on the caller’s (or sender’s) side observes that the destination client is offline, it will notify the application that it is required to trigger the sending of a push notification to the callee’s device.

Push Notification Data

On startup, each instance of the application is expected to register a device identifier that push notifications can be addressed to, in the event that the application goes offline. The identifier is referred to as push notification data and should be provided to the Rebtel client via the method registerPushNotificationData:.

The push notification data consists of an arbitrary byte sequence and it is up to the application developer to define its structure and what it contains. It should contain enough information to allow a push service to send the push to a particular user of the application on a particular device. For example, an iOS exclusive application would likely use the Apple Push Notification Device Token as its push notification data. Multi-platform applications may use a mix of different push services. The following sections will assume that Apple Remote Notifications are used, but the usage pattern for other push services would be similar.

The push notification data can also be unregistered by calling the unregisterPushNotificationData method. This will effectively disable incoming calls via push notifications for the particular device.

Enable Push Notifications

Start by enabling support for push notifications when initiating the REBClient:

#include "RebtelSDK.h"

id<REBClient> client = [RebtelSDK clientWithApplicationKey:@"<application key>" 
                                         applicationSecret:@"<application secret>"
                                           environmentHost:@"sdksandbox.rebtel.com" 
                                                    userId:@"<user id>"];

[client setSupportPushNotifications:YES];

client.delegate = ...; 

[client start];

Supporting offline calls and/or messages requires that the application registers for remote push notifications, which in the example below is done in the method -[UIApplicationDelegate application:didFinishLaunchingWithOptions:].

- (BOOL)application:(UIApplication *)app didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    UIRemoteNotificationType types = UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound;
    [[UIApplication sharedApplication] registerForRemoteNotificationTypes:types];
}

Next step is to register the push notification data with the REBClient, which in the example below is done by simply using the APNS device token as push notification data. Upon receiving the the device token from Apple Push Notification Service via the UIApplicationDelegate-method, it is registered with the REBClient.

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    // get previously initiated Rebtel client
    id<REBClient> client = [self rebtelClient];

    [client registerPushNotificationData:deviceToken];
}

Please refer to Apple’s Local and Push Notification Programming Guide for more information on how to obtain the Apple Push Notification Device Token.

Sending and Receiving Apple Push Notifications

Figure 3. Simplified view of incoming calls when offline.
Figure 3. Simplified view of incoming calls when offline.

If the application is not in the foreground when there’s an incoming call or message, it needs to be notified that there is an incoming event to act on (i.e. there is a call to answer or message to view).

The caller or message sender will get an event notifying the application that a push message must be sent to the recipient. The application sends the event data to the application developer’s server, which passes the push payload on to Apple servers. Apple then sends a push notification to the recipient’s device that there is an incoming call or message to act upon.

The application developer must have a server that is configured for sending push notifications to Apple Push Notification Service. Please see the Rebtel REST API User Guide for details on how to handle feedback from Apple Push Notification Service.

Please refer to Apple’s Local and Push Notification Programming Guide for further details on push notifications.

On ”A” Side

When the recipient’s application is offline and it is required to notify the user via a push notification, the caller’s or sender’s application will be notified via the delegate method call:shouldSendPushNotifications:.

As there might be multiple registered devices for the recipient user (e.g. the same user is using the application on both an iPhone and an iPad) the callback is passed an array of REBPushPairs. The pairs contain a payload that is Rebtel- and call-specific. Moreover the pairs contain a push data byte array. The Rebtel specific payload should be embedded in the push notification sent to the recipient’s device(s). The push data is the same push data that the recipient’s application registered earlier.

- (void)call:(id<REBCall>)call shouldSendPushNotifications:(NSArray *) pushPairs {
    // Send payload and push data to application server
    // which should communicate with Apple Push Notification Service
    // to send push notifications.
}

NOTE: The example above specifically shows the calling case. The messaging case is analogous.

A push notification should be sent to each device, where each pushPair.pushData entry in the array corresponds to one device. The push notification should include the Rebtel-specific payload so it can be forwarded to the Rebtel client running on the destination device.

The Rebtel-specific payload should be embedded as custom payload data in the Apple Push Notification Payload, see JSON example below.

{
    "aps" : {
        "alert" : "Incoming call from <user>",
        "sound" : "bingbong.aiff"
    },
    "REB" : <payload>,
}

The Rebtel-specific payload will not exceed 100 bytes, meaning that there should be 156 bytes still available in the push notification payload for application specific purposes.

Please refer to Apple’s Local and Push Notification Programming Guide for further details.

On ”B” Side

As a prerequisite, offline calling and messaging must have been enabled on the receiver’s side (see Push Notifications).

When the application receives a push notification from the Apple Push Notification Service, the application launches and extracts the Rebtel-specific payload from the push notification. Once extracted the payload is forwarded to the Rebtel client via the method relayRemotePushNotificationPayload:.

- (BOOL)application:(UIApplication *)app didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    NSDictionary* remotePush = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];

    if (remotePush) {

        // Extract the Rebtel-specific payload from the Apple Remote Push Notification
        NSString* payload = [remotePush objectForKey:@"REB"]; 

        // Get previously initiated Rebtel client
        id<REBClient> client = [self rebtelClient];

        id<REBNotificationResult> result = [client relayRemotePushNotificationPayload:payload];

        if ([result isCall] && result.isTimedOut) {
            // Present alert notifying about missed call
        } else if (!result.isValid) {
            // Handle error                
        }
    }
}

Note: You should have similar logic of relaying the push notification payload to the REBClient-instance in your implementation of -[UIApplicationDelegate application: didReceiveRemoteNotification:].

Application Authentication

When initiating a Rebtel client, a user identity should be provided. The first time the application instance and the Rebtel client is running on behalf of a particular user, it is required to register against the Rebtel service. This is something that is mainly handled transparently by the Rebtel SDK, but also works a bit differently depending on which authentication scheme the developer chooses to use.

The step of registering a user identity against the Rebtel service requires the application instance to be authenticated and authorized to perform the user registration. Once the application instance has successfully registered the user identity, it will also have obtained the necessary credentials to perform further authorized requests for that specific user, i.e. calling.

Two different authentication schemes are available as described below.

Authentication by Client Access to Application Secret

This application authentication scheme is based on giving the application direct access to the Application Secret, which enables the Rebtel Client SDK in the application to self-sign an authorized request to perform user registration. Choosing this authentication scheme corresponds to initiating the Rebtel client by using the factory method that takes both an Application Key and an Application Secret.

Using this authentication scheme is the quickest to get started with as the client application instances can directly perform authorized requests against the Rebtel service.

Note: It is not recommended to have the application secret in plain text in the source code in the release version of the application.

Authentication Supported by Application Server

This application authentication scheme is based on the client application instance not having direct access to the Application Secret. Instead, when the Rebtel client needs to perform an authorized request to register a user identity against the Rebtel service, it will need to be provided with an authentication signature and a registration sequence to perform the registration. This should be provided by the application’s backend service, e.g. via a HTTP request over an SSL connection.

This scheme has the benefit of the application secret never being directly accessible by the client applications and gives a better level of security as well as flexibility.

Note: The need for the Rebtel client to request an authentication signature and registration sequence is only required once per user and device. Not on every application launch.

Figure 4. Authentication Supported by Application Server
Figure 4. Authentication Supported by Application Server

Generating the Signature

The Application Server is responsible for generating a valid signature for each registration request that it accepts as a valid user registration. The sequence is a cryptographic nonce, and must be a monotonically increasing value. The signature is then generated as as follows (pseudogrammar):

string userId;
string applicationKey; // E.g. "196087a1-e815-4bc4-8984-60d8d8a43f1d"
string applicationSecret; // E.g. "oYdgGRXoxEuJhGDY2KQ/HQ=="
uint64 sequence = previous_sequence + 1; // E.g. previous_sequence = 0

string stringToSign = userId + applicationKey + sequence + applicationSecret;

// Use a Base64-encoder that don't introduce line-breaks, 
// or trim the output signature afterwards.
string signature = Base64.encode(SHA1.digest(stringToSign));

E.g. in Java:

    // Generating the Signature - Java
    // import java.security.MessageDigest;
    // import org.apache.commons.codec.binary.Base64;

    String userId; 
    String applicationKey; // E.g. "196087a1-e815-4bc4-8984-60d8d8a43f1d";
    String applicationSecret; // E.g. "oYdgGRXoxEuJhGDY2KQ/HQ==";
    long sequence; // fetch and increment last used sequence

    String toSign = userId + applicationKey + sequence + applicationSecret;

    MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");
    byte[] hash = messageDigest.digest(toSign.getBytes("UTF-8"));

    String signature = Base64.encodeBase64String(hash).trim();

Starting the Client and Providing Authorization credentials for User Registration

// Instantiate a client object using the client factory method.
id<REBClient> client = [RebtelSDK clientWithApplicationKey:@"<application key>" 
                                           environmentHost:@"sdksandbox.rebtel.com" 
                                                    userId:@"<user id>"];

client.delegate = ...;

[client start];

// This will on the first run for this user, call 
// -[REBClientDelegate client:requiresRegistrationCredentials:], 
// which implementations could look something like this:

- (void)client:(id<REBClient>)client 
requiresRegistrationCredentials:(id<REBClientRegistration>) registrationCallback {

  // Perform API request to server which keeps the Application Secret
  [myAPIService getAuthorizedSignatureForUser:[client userId]
  onSuccess:^(NSString* signature, long long sequence){

    // Forward the signature and sequence back into Rebtel SDK
    [registrationCallback registerWithSignature:signature sequence:sequence];
  }
  onFailure:^(NSError* error) {

    // Forward potential network request error to Rebtel SDK, 
    // e.g. failure due to no internet connection.
    [registrationCallback registerDidFail:error];
  }];
}

Miscellaneous

Sample applications

We have included sample applications that show how to build very basic apps using the Rebtel SDK. The code can be found in the “/samples” folder. To be able to call or message with the sample applications, just enter a valid application key and a valid application secret in RebtelSDK class.

Note: This is only meant as small samples, and should not be seen as a complete example how to build a calling or messaging apps.

Encryption Export and App Store

Please make sure to check the Summary of U.S. Export Controls Applicable to Commercial Encryption Products and ensure that the application is registered for the Encryption Regulations, if applicable. It can be found under this link.

Statistics

The Rebtel SDK client will upload statistics to the Rebtel servers at the end of a call, a call failure or similar event. The statistics are used for monitoring of network status, call quality and other aspects regarding the general quality of the service.

Some of the information is not anonymous and may be associated with the User ID call participants.

The statistics upload is done by the client in the background.

Support

If you need support, please visit our community or send an email to sdk@rebtel.com.

iOS 7 Compatibility Guidelines

Request User Permission for Using the Microphone

In iOS 7, additional user privacy constraints are enforced which requires the application to be granted permission to use the device microphone. Unless the application has explicitly requested permission to use the microphone, the user will be shown a dialog the first time the microphone is activated.

In the context of the Rebtel SDK, this will occur once the first call is established unless the application has been granted permission before. We strongly recommend to request permission to use microphone explicitly in your application, at an appropriate time in terms of user interaction, and not rely on that the permission dialog is shown as a consequence of the first Rebtel call is established. By explicitly requesting permission via the methods available iOS SDK, the application will have more control over when the dialog is shown to the user, which will result in a better user experience. Please see the Apple iOS SDK documentation on the class AVAudioSession for details on how request permission to use the microphone.

Third Party Libraries and Copyright

All Third Party Libraries and Copyright notices can be found under this link.