-
Notifications
You must be signed in to change notification settings - Fork 0
MigrationGuide from 1.1.1 to 1.2.0
This guide discusses migration from BandyerSDK version 1.1.x to version 1.2.0
In order to accomodate requirements changes in the iOS 13.0 SDK the BandyerSDK version 1.2.0 changes the way VoIP push notifications are handled. Since version 1.0.0 the burden of handling VoIP push notifications was one of the application developer responsibility. From version 1.2.0 the BandyerSDK will take that responsibility, handling VoIP push notifications for you, although you are still responsible for registering the device push token with your back-end system and for sending the push notification payload from your back-end to APNS. The following steps will guide you through the changes you need to make to your code.
Starting from version 1.2.0 you don't need to instantiate and configure an instance of PKPushRegistry anymore. The SDK will create an instance of the registry on behalf of your app at the most appropriate time and it will respond to delegate messages received from it. It is likely that you create one registry instance in your AppDelegate or in some other classes retaining the app state. You should remove any code related to PKPushRegistry except for the code handling Push Token registration with your servers (More on that, in the following chapter).
Let's pretend you have some code in your AppDelegate that creates an instance of a PKPushRegistry in your AppDelegate like so:
#import <PushKit/PushKit.h>
#import <BandyerSDK/BandyerSDK.h>
#import "AppDelegate.h"
@interface AppDelegate () <PKPushRegistryDelegate>
@property (nonatomic, strong, nullable) PKPushRegistry *registry;
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
BDKConfig *config = [BDKConfig new];
config.environment = BDKEnvironment.sandbox;
[BandyerSDK.instance initializeWithApplicationId:@"YOUR_APP_ID" config:config];
//Other configuration code[...]
self.registry = [[PKPushRegistry alloc] initWithQueue:dispatch_get_main_queue()];
self.registry.delegate = self;
self.registry.desiredPushTypes = [NSSet setWithObject:PKPushTypeVoIP];
return YES;
}
[...]
@end
import UIKit
import PushKit
import BandyerSDK
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var registry: PKPushRegistry?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let config = BDKConfig()
config.environment = .sandbox
BandyerSDK.instance().initialize(withApplicationId: "YOUR_APP_ID", config: config)
//Other configuration code [...]
registry = PKPushRegistry(queue: .main)
registry?.delegate = self
registry?.desiredPushTypes = [.voIP]
return true
}
[...]
}
After updating to version 1.2.0 you should get rid of any code related to PKPushRegistry
#import <PushKit/PushKit.h>
#import <BandyerSDK/BandyerSDK.h>
#import "AppDelegate.h"
@interface AppDelegate () <PKPushRegistryDelegate>
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
BDKConfig *config = [BDKConfig new];
config.environment = BDKEnvironment.sandbox;
[BandyerSDK.instance initializeWithApplicationId:@"YOUR_APP_ID" config:config];
//Other configuration code[...]
return YES;
}
[...]
@end
import UIKit
import PushKit
import BandyerSDK
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let config = BDKConfig()
config.environment = .sandbox
BandyerSDK.instance().initialize(withApplicationId: "YOUR_APP_ID", config: config)
//Other configuration code [...]
return true
}
[...]
}
We take for granted you have one class in your code conforming to the PKPushRegistryDelegate protocol. You can safely remove pushRegistry(_:didReceiveIncomingPushWith:for:completion:) and pushRegistry(_:didReceiveIncomingPushWith:for:) methods since push notifications will be handled by the BandyerSDK. You must NOT remove neither pushRegistry(_:didUpdate:for:) method nor the class conforming to PKPushRegistryDelegate protocol, because the BandyerSDK is not going to handle the device push token registration with your back-end system for you. You are still responsible for registering the device push token received in pushRegistry(_:didUpdate:for:) method with your back-end system. You might be wondering, "How am i supposed to get the device push token, if you made me remove the PKPushRegistry instance from my code?", well, in the nexts chapter you'll find what must be done in order to get those device push tokens.
Let's pretend you have some code in your AppDelegate that looks like the following:
#import <PushKit/PushKit.h>
#import <BandyerSDK/BandyerSDK.h>
#import "AppDelegate.h"
@interface AppDelegate () <PKPushRegistryDelegate, BCXCallClientObserver>
@property (nonatomic, strong, nullable) PKPushPayload *pendingPayload;
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//Application setup code [...]
return YES;
}
- (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)pushCredentials forType:(PKPushType)type
{
//Update push token credential in your back-end system
NSLog(@"Updated push credentials %@", pushCredentials.token);
}
- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(PKPushType)type
{
if (BandyerSDK.instance.callClient.state == BCXCallClientStateRunning)
{
[self handlePushPayload:payload];
} else
{
self.pendingPayload = payload;
[BandyerSDK.instance.callClient resume];
}
}
- (void)handlePushPayload:(PKPushPayload *)payload
{
NSDictionary *dictionaryPayload = payload.dictionaryPayload;
NSDictionary *incomingCallPayload = [dictionaryPayload valueForKeyPath:@"KEYPATH_TO_DATA_DICTIONARY"];
[BandyerSDK.instance.callClient handleNotification:incomingCallPayload];
}
[...]
@end
import UIKit
import PushKit
import BandyerSDK
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var pendingPayload: PKPushPayload?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
//Application setup code [...]
return true
}
func handlePushPayload(_ payload:PKPushPayload?){
guard let p = payload else {
return
}
let dictionaryPayload = p.dictionaryPayload as NSDictionary
let incomingCallPayload = dictionaryPayload.value(forKeyPath: "KEYPATH_TO_DATA_DICTIONARY") as! [AnyHashable : Any]
BandyerSDK.instance().callClient.handleNotification(incomingCallPayload)
}
}
extension AppDelegate : PKPushRegistryDelegate{
func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
let token = pushCredentials.token.map { String(format: "%02.2hhx", $0) }.joined()
debugPrint("Push credentials updated \(token), you should send them to your backend system")
}
func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType) {
switch BandyerSDK.instance().callClient.state {
case .running:
//If the client is running we hand it the push payload
handlePushPayload(payload)
case .paused:
pendingPayload = payload
BandyerSDK.instance().callClient.resume()
default:
pendingPayload = payload
}
}
}
[...]
After updating to version 1.2.0, your code should look like the following:
#import <PushKit/PushKit.h>
#import <BandyerSDK/BandyerSDK.h>
#import "AppDelegate.h"
@interface AppDelegate () <PKPushRegistryDelegate, BCXCallClientObserver>
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//Application setup code [...]
return YES;
}
[...]
// This is the only method that must be left untouched of your PKPushRegistryDelegate
- (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)pushCredentials forType:(PKPushType)type
{
//Send the device push token to your back-end system.
NSLog(@"Updated push credentials %@", pushCredentials.token);
}
import UIKit
import PushKit
import BandyerSDK
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
//Application setup code [...]
return true
}
}
[...]
extension AppDelegate : PKPushRegistryDelegate{
// This is the only method that must be left untouched of your PKPushRegistryDelegate
func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
// Send the device push token to your back-end system.
let token = pushCredentials.token.map { String(format: "%02.2hhx", $0) }.joined()
debugPrint("Push credentials updated \(token), you should send them to your backend system")
}
}
The BDKConfig class has been updated adding two properties pushRegistryDelegate and notificationPayloadKeyPath. If you are opting-in to support CallKit in your app you must populate those properties before initializing the BandyerSDK, otherwise you'll get a runtime exception. You must provide an object conforming to PKPushRegistryDelegate protocol, in the pushRegistryDelegate property of the configuration object. Whenever the PKPushRegistry notifies the BandyerSDK that a device push token has been updated, the BandyerSDK will notify the object provided accordingly. The notificationPayloadKeyPath property must contain the key path at which the bandyer payload can be found when a VoIP push notification is received.
Same as before let's pretend you configure the BandyerSDK in your AppDelegate like so:
#import <PushKit/PushKit.h>
#import <BandyerSDK/BandyerSDK.h>
#import "AppDelegate.h"
@interface AppDelegate () <PKPushRegistryDelegate>
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
BDKEnvironment *env = BDKEnvironment.sandbox;
BDKConfig *config = [BDKConfig new];
config.environment = env;
config.callKitEnabled = YES;
config.nativeUILocalizedName = @"My wonderful app";
config.nativeUITemplateIconImageData = UIImagePNGRepresentation([UIImage imageNamed:@"callkit-icon"]);
[BandyerSDK.instance initializeWithApplicationId:@"YOUR_APP_ID" config:config];
//Other configuration code [...]
return YES;
}
[...]
import UIKit
import PushKit
import BandyerSDK
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let config = BDKConfig()
config.environment = .sandbox
config.isCallKitEnabled = true
config.nativeUILocalizedName = "My wonderful app"
config.nativeUITemplateIconImageData = UIImage(named: "callkit-icon")?.pngData()
BandyerSDK.instance().initialize(withApplicationId: "YOUR_APP_ID", config: config)
//Other configuration code [...]
return true
}
[...]
}
After updating to version 1.2.0, your configuration code should look like this:
#import <PushKit/PushKit.h>
#import <BandyerSDK/BandyerSDK.h>
#import "AppDelegate.h"
@interface AppDelegate () <PKPushRegistryDelegate>
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
BDKConfig *config = [BDKConfig new];
config.environment = BDKEnvironment.sandbox;
config.callKitEnabled = YES;
config.nativeUILocalizedName = @"My wonderful app";
config.nativeUITemplateIconImageData = UIImagePNGRepresentation([UIImage imageNamed:@"callkit-icon"]);
//Here we are telling the BandyerSDK which object must be called when device push tokens are updated
config.pushRegistryDelegate = self;
//Here we are telling the BandyerSDK where to look for the expected payload in the push notification dictionary payload
config.notificationPayloadKeyPath = @"custom.data"; //This value is fake, it depends on how the notification payload is embedded in the push notification dictionary from your back-end system
[BandyerSDK.instance initializeWithApplicationId:@"YOUR_APP_ID" config:config];
//Other configuration code [...]
return YES;
}
[...]
import UIKit
import PushKit
import BandyerSDK
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let config = BDKConfig()
config.environment = .sandbox
config.isCallKitEnabled = true
config.nativeUILocalizedName = "My wonderful app"
config.nativeUITemplateIconImageData = UIImage(named: "callkit-icon")?.pngData()
//Here we are telling the BandyerSDK which object must be called when device push tokens are updated
config.pushRegistryDelegate = self
//Here we are telling the BandyerSDK where to look for the expected payload in the push notification dictionary payload
config.notificationPayloadKeyPath = "custom.data" //This value is fake, it depends on how the notification payload is embedded in the push notification dictionary from your back-end system
BandyerSDK.instance().initialize(withApplicationId: "YOUR_APP_ID", config: config)
//Other configuration code [...]
return true
}
[...]
}
The final result should something that looks like this:
#import <PushKit/PushKit.h>
#import <BandyerSDK/BandyerSDK.h>
#import "AppDelegate.h"
@interface AppDelegate () <PKPushRegistryDelegate, BCXCallClientObserver>
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
BDKConfig *config = [BDKConfig new];
config.environment = BDKEnvironment.sandbox;
config.callKitEnabled = YES;
config.nativeUILocalizedName = @"My wonderful app";
config.nativeUITemplateIconImageData = UIImagePNGRepresentation([UIImage imageNamed:@"callkit-icon"]);
//Here we are telling the BandyerSDK which object must be called when device push tokens are updated
config.pushRegistryDelegate = self;
//Here we are telling the BandyerSDK where to look for the expected payload in the push notification dictionary payload
config.notificationPayloadKeyPath = @"custom.data"; //This value is fake, it depends on how the notification payload is embedded in the push notification dictionary from your back-end system
[BandyerSDK.instance initializeWithApplicationId:@"YOUR_APP_ID" config:config];
//Application setup code [...]
return YES;
}
[...]
// This is the only method that must be left untouched of your PKPushRegistryDelegate
- (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)pushCredentials forType:(PKPushType)type
{
//Send the device push token to your back-end system.
NSLog(@"Updated push credentials %@", pushCredentials.token);
}
@end
import UIKit
import PushKit
import BandyerSDK
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let config = BDKConfig()
config.environment = .sandbox
config.isCallKitEnabled = true
config.nativeUILocalizedName = "My wonderful app"
config.nativeUITemplateIconImageData = UIImage(named: "callkit-icon")?.pngData()
//Here we are telling the BandyerSDK which object must be called when device push tokens are updated
config.pushRegistryDelegate = self
//Here we are telling the BandyerSDK where to look for the expected payload in the push notification dictionary payload
config.notificationPayloadKeyPath = "custom.data" //This value is fake, it depends on how the notification payload is embedded in the push notification dictionary from your back-end system
BandyerSDK.instance().initialize(withApplicationId: "YOUR_APP_ID", config: config)
//Application setup code [...]
return true
}
}
[...]
extension AppDelegate : PKPushRegistryDelegate{
// This is the only method that must be left untouched of your PKPushRegistryDelegate
func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
// Send the device push token to your back-end system.
let token = pushCredentials.token.map { String(format: "%02.2hhx", $0) }.joined()
debugPrint("Push credentials updated \(token), you should send them to your backend system")
}
}
Starting from version 1.2.0 we introduced a UIWindow subclass you can use to present the call UI in front of any content your application is displaying. The migration process to using the BDKCallWindow instead of the BDKCallViewController is straightforward. The BDKCallWindow has the same public interface of the view controller, so you should have to make very little changes in your code. Let's begin with some code, shall we?
In the previous versions of the SDK whenever you had to present the Bandyer Call UI, whether for an incoming call, an outgoing call or a call initiated from an URL, you had to instantiate a BDKCallViewController, provide it a configuration object and the present it modally on your presenting view controller, like so:
- (void)presentCallViewController:(id<BDKIntent>)intent
{
BDKCallViewControllerConfiguration *config = [BDKCallViewControllerConfiguration new];
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle]
pathForResource:@"SampleVideo_640x360_10mb" ofType:@"mp4"]];
config.fakeCapturerFileURL = url;
BDKCallViewController *controller = [[BDKCallViewController alloc] init];
controller.delegate = self;
[controller setConfiguration:config];
[controller handleIntent:intent];
[self presentViewController:controller animated:YES completion:NULL];
}
func presentCallViewController(intent: BDKIntent) {
let config = CallViewControllerConfiguration()
let filePath = Bundle.main.path(forResource: "SampleVideo_640x360_10mb", ofType: "mp4")
guard let path = filePath else {
fatalError("The fake file for the file capturer could not be found")
}
config.fakeCapturerFileURL = URL(fileURLWithPath:path)
let controller = CallViewController()
controller.delegate = self
controller.setConfiguration(config)
controller.handle(intent: intent)
present(controller, animated: true, completion: nil)
}
Using the call window is almost identical, but we use the window instead of the view controller:
- (void)presentCallWindow:(id<BDKIntent>)intent
{
[self createCallWindowIfNeeded];
BDKCallViewControllerConfiguration *config = [BDKCallViewControllerConfiguration new];
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"SampleVideo_640x360_10mb" ofType:@"mp4"]];
config.fakeCapturerFileURL = url;
[self.callWindow setConfiguration:config];
[self.callWindow shouldPresentCallViewControllerWithIntent:intent completion:^(BOOL succeeded) {
}];
}
- (void)createCallWindowIfNeeded
{
//You must keep only one reference to the call window in order to avoid the reset of BDKCallViewController.
if (self.callWindow)
{
return;
}
//Please be sure to have in memory only one instance of BDKCallWindow, otherwise an exception will be thrown.
if (BDKCallWindow.instance)
{
self.window = BDKCallWindow.instance;
} else
{
self.window = [[BDKCallWindow alloc] init];
}
self.window.callDelegate = self;
}
//-------------------------------------------------------------------------------------------
#pragma mark - Call window delegate
//-------------------------------------------------------------------------------------------
- (void)callWindowDidFinish:(BDKCallWindow *)window
{
self.callWindow.hidden = YES;
}
- (void)callWindow:(BDKCallWindow *)window openChatWith:(BCHOpenChatIntent *)intent
{
self.callWindow.hidden = YES;
//Present the chat interface [...]
}
lazy var callWindow: CallWindow = {
var window: CallWindow
if CallWindow.instance != nil {
window = CallWindow.instance!
} else {
window = CallWindow()
}
window.callDelegate = self
return window
}()
func presentCallWindow(intent: BDKIntent){
let config = CallViewControllerConfiguration()
let filePath = Bundle.main.path(forResource: "SampleVideo_640x360_10mb", ofType: "mp4")
guard let path = filePath else {
fatalError("The fake file for the file capturer could not be found")
}
config.fakeCapturerFileURL = URL(fileURLWithPath:path)
callWindow.setConfiguration(config)
callWindow.shouldPresentCallViewController(intent: intent) { success in
}
}
//MARK: Call window delegate
func callWindowDidFinish(_ window: CallWindow) {
window.isHidden = true
}
func callWindow(_ window: CallWindow, openChatWith intent: OpenChatIntent) {
//Present the chat interface [...]
}
Inside the callback of the new method shouldPresentCallViewController you should manage the failure of presentation, i.e. if you try to start a new call while there is an ongoing call whit different caller.
When there is an ongoing call, your view controller could show a banner to highlight the presence of that call.
To enable this feature, you must use the new CallBannerController class.
//Reference the controller inside your view controller
@property (nonatomic, strong) BCKCallBannerController *callBannerController;
//Alloc and init it
- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullable NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
{
_callBannerController = [BCKCallBannerController new];
}
return self;
}
//Setup it inside viewDidLoad
- (void)viewDidLoad
{
[super viewDidLoad];
self.callBannerController.delegate = self;
self.callBannerController.parentViewController = self;
}
//Manage the possibility to show/hide the banner view
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.callBannerController show];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[self.callBannerController hide];
}
//-------------------------------------------------------------------------------------------
#pragma mark - Call Banner Controller delegate
//-------------------------------------------------------------------------------------------
- (void)callBannerController:(BCKCallBannerController *_Nonnull)controller willHide:(BCKCallBannerView *_Nonnull)banner
{
//Here you must handle the state of your view controller accordingly, for example managing the status bar appearance
}
- (void)callBannerController:(BCKCallBannerController *_Nonnull)controller willShow:(BCKCallBannerView *_Nonnull)banner
{
//Here you must handle the state of your view controller accordingly, for example managing the status bar appearance
}
- (void)callBannerController:(BCKCallBannerController *_Nonnull)controller didTouch:(BCKCallBannerView *_Nonnull)banner
{
//Here you must present Call View controller
}
//Reference the controller inside your view controller and init it
private let callBannerController = CallBannerController()
//Setup it inside viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
callBannerController.delegate = self
callBannerController.parentViewController = self
}
//Manage the possibility to show/hide the banner view
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
callBannerController.show()
}
override public func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
callBannerController.hide()
}
//MARK: Call banner controller delegate
public func callBannerController(_ controller: CallBannerController, willHide banner: CallBannerView) {
//Here you must handle the state of your view controller accordingly, for example managing the status bar appearance
}
public func callBannerController(_ controller: CallBannerController, willShow banner: CallBannerView) {
//Here you must handle the state of your view controller accordingly, for example managing the status bar appearance
}
public func callBannerController(_ controller: CallBannerController, didTouch banner: CallBannerView) {
//Here you must present Call View controller
}
Starting from version 1.2.0, we introduce a new functionality, the chat, allowing one-to-one messages exchanging.
In order to start the Chat Client, you need to call the start method of the chat client from BandyerSDK instance.
//Here we start the chat client, providing the "user alias" of the current user.
[BandyerSDK.instance.chatClient start:@"current user alias"];
//Here we start the chat client, providing the "user alias" of the current user.
BandyerSDK.instance().chatClient.start("current user alias")
You can subscribe/unsubscribe to chat client lifecycle as an observer following this snippet:
//Subscribing
[BandyerSDK.instance.chatClient addObserver:self queue:dispatch_get_main_queue()];
//Unsubscribing
[BandyerSDK.instance.chatClient removeObserver:self];
//-------------------------------------------------------------------------------------------
#pragma mark - Chat Client observer
//-------------------------------------------------------------------------------------------
- (void)chatClientWillStart:(id <BCHChatClient>)client
{
//Invoked when the chat client will start.
}
- (void)chatClientDidStart:(id <BCHChatClient>)client
{
//Invoked when the chat client is started successfully and it is connected to Bandyer platform.
}
- (void)chatClientWillPause:(id <BCHChatClient>)client
{
//Invoked when the chat client will pause.
}
- (void)chatClientDidPause:(id <BCHChatClient>)client
{
//Invoked when the chat client did pause.
}
- (void)chatClientWillStop:(id <BCHChatClient>)client
{
//Invoked when the chat client will stop.
}
- (void)chatClientDidStop:(id <BCHChatClient>)client
{
//Invoked when the chat client is stopped.
}
- (void)chatClientWillResume:(id <BCHChatClient>)client
{
//Invoked when the chat client is starting to resume.
}
- (void)chatClientDidResume:(id <BCHChatClient>)client
{
//Invoked when the chat client did resume successfully.
}
- (void)chatClient:(id <BCHChatClient>)client didFailWithError:(NSError *)error
{
//Invoked when the chat client fails because of a fatal error.
}
//Subscribing
BandyerSDK.instance().chatClient.add(observer: self, queue: DispatchQueue.main)
//Unsubscribing
BandyerSDK.instance().chatClient.remove(observer: self)
//MARK: Chat Client observer
public func chatClientWillStart(_ client: BCHChatClient) {
//Invoked when the chat client will start.
}
public func chatClientDidStart(_ client: BCHChatClient) {
//Invoked when the chat client is started successfully and it is connected to Bandyer platform.
}
public func chatClientWillPause(_ client: BCHChatClient) {
//Invoked when the chat client will pause.
}
public func chatClientDidPause(_ client: BCHChatClient) {
//Invoked when the chat client did pause.
}
public func chatClientWillStop(_ client: BCHChatClient) {
//Invoked when the chat client will stop.
}
public func chatClientDidStop(_ client: BCHChatClient) {
//Invoked when the chat client is stopped.
}
public func chatClientWillResume(_ client: BCHChatClient) {
//Invoked when the chat client is starting to resume.
}
public func chatClientDidResume(_ client: BCHChatClient) {
//Invoked when the chat client did resume successfully.
}
public func chatClient(_ client: BCHChatClient, didFailWithError error: Error) {
//Invoked when the chat client fails because of a fatal error.
}
In order to present the ChannelViewController, there are two possibilities, from a ChatNotification instance or from a participant id String.
Following, this is an example of creating a OpenChatIntent with notification and string:
BCHChatNotification *notification; //this is your notification instance
//here we are creating a BCHOpenChatIntent instance
BCHOpenChatIntent *intent = [BCHOpenChatIntent openChatFrom:notification];
NSString * participantId; //this is the counterpart id
//here we are creating a BCHOpenChatIntent instance
BCHOpenChatIntent *intent = [BCHOpenChatIntent openChatWith: participantId];
let notification: ChatNotification = //this is your notification instance
//here we are creating a OpenChatIntent instance
let intent = OpenChatIntent.openChat(from: notification)
let participantId = "" //this is the counterpart id
//here we are creating a OpenChatIntent instance
let intent = OpenChatIntent.openChat(with: participantId)
Once you have your intent instance, you can initialise the view controller:
BCHOpenChatIntent *intent; //this is your intent instance
BCHChannelViewController *channelViewController = [[BCHChannelViewController alloc] init];
channelViewController.delegate = self;
channelViewController.intent = intent;
//-------------------------------------------------------------------------------------------
#pragma mark - Channel view controller delegate
//-------------------------------------------------------------------------------------------
- (void)channelViewControllerDidFinish:(BCHChannelViewController *)controller
{
//Here you must dismiss BCHChannelViewController
}
- (void)channelViewController:(BCHChannelViewController *)controller didTapAudioCallWith:(NSArray *)users
{
//Here you must present Call View controller
}
- (void)channelViewController:(BCHChannelViewController *)controller didTapVideoCallWith:(NSArray *)users
{
//Here you must present Call View controller
}
- (void)channelViewController:(BCHChannelViewController *)controller didTouchNotification:(BCHChatNotification *)notification
{
//Here you must present Chat View controller
}
- (void)channelViewController:(BCHChannelViewController *)controller willHide:(BCKCallBannerView *)banner
{
//Here you must handle the state of your view controller accordingly, for example managing the status bar appearance
}
- (void)channelViewController:(BCHChannelViewController *)controller willShow:(BCKCallBannerView *)banner
{
//Here you must handle the state of your view controller accordingly, for example managing the status bar appearance
}
- (void)channelViewController:(BCHChannelViewController *)controller didTouchBanner:(BCKCallBannerView *)banner
{
//Here you must present Call View controller
}
let intent: OpenChatIntent = //this is your intent instance
let channelViewController = ChannelViewController()
channelViewController.delegate = self
channelViewController.intent = intent
//MARK: Channel view controller delegate
public func channelViewControllerDidFinish(_ controller: ChannelViewController) {
//Here you must dismiss BCHChannelViewController
}
public func channelViewController(_ controller: ChannelViewController, didTapAudioCallWith users: [String]) {
//Here you must present Call View controller
}
public func channelViewController(_ controller: ChannelViewController, didTapVideoCallWith users: [String]) {
//Here you must present Call View controller
}
public func channelViewController(_ controller: ChannelViewController, didTouch notification: ChatNotification) {
//Here you must present Chat View controller
}
public func channelViewController(_ controller: ChannelViewController, willHide banner: CallBannerView) {
//Here you must handle the state of your view controller accordingly, for example managing the status bar appearance
}
public func channelViewController(_ controller: ChannelViewController, willShow banner: CallBannerView) {
//Here you must handle the state of your view controller accordingly, for example managing the status bar appearance
}
public func channelViewController(_ controller: ChannelViewController, didTouch banner: CallBannerView) {
//Here you must present Call View controller
}
When a message is received, there is the possibility to see a custom in-app notification view.
To enable this feature, you must use the new MessageNotificationController class.
//Reference the controller inside your view controller
@property (nonatomic, strong) BCHMessageNotificationController *messageNotificationController;
//Alloc and init it
- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullable NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
{
_messageNotificationController = [BCHMessageNotificationController new];
}
return self;
}
//Setup it inside viewDidLoad
- (void)viewDidLoad
{
[super viewDidLoad];
self.messageNotificationController.delegate = self;
self.messageNotificationController.parentViewController = self;
}
//Manage the possibility to show/hide the notification view
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.messageNotificationController show];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[self.messageNotificationController hide];
}
//-------------------------------------------------------------------------------------------
#pragma mark - Message Notification Controller delegate
//-------------------------------------------------------------------------------------------
- (void)messageNotificationController:(BCHMessageNotificationController *)controller didTouch:(BCHChatNotification *)notification
{
//Present BCHChannelViewController
}
//Reference the controller inside your view controller and init it
private let messageNotificationController = MessageNotificationController()
//Setup it inside viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
messageNotificationController.delegate = self
messageNotificationController.parentViewController = self
}
//Manage the possibility to show/hide the notification view
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
messageNotificationController.show()
}
override public func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
messageNotificationController.hide()
}
//MARK: Message notification controller delegate
public func messageNotificationController(_ controller: MessageNotificationController, didTouch notification: ChatNotification) {
//Present ChannelViewController
}
Looking for other platforms? Take a look at Android, Flutter, ReactNative, Ionic / Cordova. Anything unclear or inaccurate? Please let us know by submitting an Issue or write us here.