Receiving an incoming call 1.x - Bandyer/Bandyer-iOS-SDK GitHub Wiki

This guide will show you how to handle incoming calls and how to show the Bandyer call UI in your app.

Table of contents

Overview

From now on we are assuming you read the previous guides regarding the call client initialization and its lifecycle. We are also assuming the Bandyer call client is started.

The steps required to handle an incoming call are the following:

  1. Register as an observer on the call client
  2. Implement the BCXCallClientObserver protocol callClient:didReceiveIncomingCall: method
  3. Create or retrieve the call window
  4. Provide a BDKIncomingCallHandlingIntent to the call window

The BDKIncomingCallHandlingIntent

When making an outgoing call you are required to create an intent that will contain all the information the SDK needs to place the outgoing call. The same is true when you need to handle an incoming call. The BandyerSDK provides a BDKIncomingCallHandlingIntent you must instantiate and give to the Call window before the UI can be presented. The difference between a BDKMakeCallIntent and a BDKIncomingCallHandlingIntent is that the latter doesn't provide any properties you can set on it.

The BDKCallWindow

Once you created the intent for the call, it's time to create the window where the call user interface will be presented. Starting from version 1.2.0 we introduced a subclass of UIWindow that will present the call UI in front of any content your app is displaying. This window will take care of creating and present a call view controller for you. In your code you need to do three things: create the call window (or retrieve it if you have created one already), provide the call window a configuration object, tell it to handle the previously created intent. As you might recall from the Making an outgoing call guide, the steps you need to make are the same that are needed to present the call user interface for an outgoing call. The code listing below are the same presented in the Making an outgoing call guide, with some small changes made for handling the incoming call received.

@implementation MyViewController

//The call window instance must be created only once and kept alive keeping a reference to it
- (BDKCallWindow *)callWindow
{
    if (!_callWindow)
    {
	if (BDKCallWindow.instance)
	{
	    _callWindow = BDKCallWindow.instance;
	} else
	{
	    //This will automatically save the new instance inside BDKCallWindow.instance.
	    _callWindow = [[BDKCallWindow alloc] init];
	}
	_callWindow.callDelegate = self;
    }
	
    return _callWindow;	
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    //When the view loads we register as a client observer, in order to receive notifications about incoming calls received and client state changes.
    [BandyerSDK.instance.callClient addObserver:self queue:dispatch_get_main_queue()];
}

#pragma mark - BCXCallClientObserver 

- (void)callClient:(id <BCXCallClient>)client didReceiveIncomingCall:(id <BCXCall>)call
{
    //When the call client notify an incoming call is received we handle it showing the user interface for it
    [self handleIncomingCall];
}

- (void)handleIncomingCall
{
    //First we create the view controller configuration object
    BDKCallViewControllerConfiguration *config = [BDKCallViewControllerConfiguration new];

    NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"SIMULATOR MP4 VIDEO" ofType:@"mp4"]];
    config.fakeCapturerFileURL = url;
    
    //Then we set the configuration object on the call window   
    [self.callWindow setConfiguration:config];

    //We create the incoming call handling intent
    BDKIncomingCallHandlingIntent* intent = [[BDKIncomingCallHandlingIntent alloc] init];

    //Finally, we present the call view controller on the window
    [self.callWindow presentCallViewControllerFor:intent completion:^(NSError * error) {
         if ([error.domain isEqualToString:BDKCallPresentationErrorDomain.value] && error.code == BDKCallPresentationErrorCodeAnotherCallOnGoing)
             {
                 //Another call is already in progress...
             }
             else if (error)
             {
                //Configuration error...
             }
    }];
}

#pragma mark - Call window delegate

- (void)callWindowDidFinish:(BDKCallWindow *)window
{
    self.callWindow.hidden = YES;
}

- (void)callWindow:(BDKCallWindow *)window openChatWith:(BCHOpenChatIntent *)intent
{
    NSLog(@"Open chat with intent %@", intent);
}

@end
class MyViewController: UIViewController, BCXCallClientObserver {

    //The call window instance must be created only once and kept alive keeping a reference to it
    lazy var callWindow: CallWindow = {
        var window: CallWindow
        if CallWindow.instance != nil {
            window = CallWindow.instance!
        } else {
            window = CallWindow()
        }

        window.callDelegate = self
        return window
    }()
	
    override func viewDidLoad() {
        super.viewDidLoad()
	
        //When the view loads we register as a client observer, in order to receive notifications about incoming calls received and client state changes.
        BandyerSDK.instance().callClient.add(observer: self, queue: .main)
    }
	
    public func callClient(_ client: BCXCallClient, didReceiveIncomingCall call: BCXCall) {
	//When the call client notify an incoming call is received we handle it showing the user interface for it
        handleIncomingCall()
    }
	
    func handleIncomingCall() {
	//First we create the view controller configuration object
	let config = CallViewControllerConfiguration()
	let filePath = Bundle.main.path(forResource: "SIMULATOR MP4 VIDEO", ofType: "mp4")

	guard let path = filePath else {
	    fatalError("The fake file for the file capturer could not be found")
	}
		
	config.fakeCapturerFileURL = URL(fileURLWithPath:path)
		
	//Then we set the configuration object on the call window
	callWindow.setConfiguration(config)
		
	//We create the incoming call handling intent
	let intent = BDKIncomingCallHandlingIntent()
		
	//Finally, we present the call view controller on the window
    callWindow.presentCallViewController(for: intent) { error in
        guard let error = error else { return }
            
            switch error {
            case let presentationError as CallPresentationError where presentationError.errorCode == CallPresentationErrorCode.anotherCallOnGoing.rawValue:
                //Another call is already in progress...
            default:
                //Configuration error...
            }
        } 
    }
}

extension MyViewController: CallWindowDelegate {
    func callWindowDidFinish(_ window: CallWindow) {
        window.isHidden = true
    }

    func callWindow(_ window: CallWindow, openChatWith intent: OpenChatIntent) {
    	print("Open chat with intent \(intent)")
    }
}

That's it! You don't have to do anything fancy, just present the call UI and you're good to go.

CallKit

As a side note, if you have enabled CallKit support in the SDK, when an incoming call is received the native system UI will be always displayed even if your application is in foreground and active.

VoIP notifications

If your app supports VoIP notifications, when an incoming call is received from notifications the call client will call the BCXCallClientObserver protocol callClient:didReceiveIncomingCall: method as if it where an incoming call received when your app is in foreground.

Where to go from here

If you followed our guides regarding the call client you should have now a complete integration with our call system. If you haven't already, we suggest you to take a look at our sample apps (objective-c swift) to see how to handle incoming calls in a real world app. You should be now ready to move to more advanced topics, like enabling CallKit support in your app, or start integrating the VoIP notifications.

What's next

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