Skip to content

Customizing user information 2.x

Marco Brescianini edited this page Aug 30, 2024 · 4 revisions

This guide will discuss how you can customize the appeareance of the BandyerSDK Call and Chat user interface changing the user information shown to the end user while taking a call or messaging through the BandyerSDK 2.0 version. If you are looking at the guide for 1.x versions please take a look at this guide instead.

Table of contents

User alias

Bandyer SDKs are designed to work with the bare minimum information about a user, this means that user contact information like first name, last name, email and so on, are not available in the SDK. We refer to a user in the Bandyer platform through hers/his "user alias", which is an alphanumeric string unique within a company (you can think of it as a slug). This approach has the advantage that we don't store any user information on our back-end, nor we send user's information over the network, but it has one drawback, whenever the end user has to be presented with contact information about another user, the only information when can show her/him is an "user alias".

custom-user-info-1

As you can see from the image above, when the call user interface is presented to the user, the BandyerSDK will show the user alias of the user by default, because it doesn't have any other information to show about the user being called. In order for the SDK to show some other user information it must be given those information and it must be told how to present them.

Fetching user information

The BandyerSDK provides a protocol named UserDetailsProvider which serve the purpose of "retrieving" the user information to be displayed when the call or the chat user interface is displayed to the end-user. In order for the BandyerSDK to use it, you must conform one of your class to the UserDetailsProvider protocol and provide an instance of that class to the BandyerSDK before initializing it or to the call (or chat) view controller configuration object before presenting it. The following snippet of code shows how the UserDetailsProvider protocol can be implemented:

import Foundation
import CallKit
import Bandyer

class Provider: UserDetailsProvider {
    private let addressBook: AddressBook
    
    init(_ addressBook: AddressBook) {
        self.addressBook = addressBook
    }

    func provideDetails(_ aliases: [String], completion: @escaping ([UserDetails]) -> Void) {
        var details = [UserDetails]()
        for alias in aliases {
            let contact = addressBook.findContact(alias: alias)
            let detail = UserDetails(alias: alias, firstname: contact?.firstName, lastname: contact?.lastName, email: contact?.email, imageURL: contact?.profileImageURL)
            details.append(detail)
        }
        
        completion(details)
    }
    
    func provideHandle(_ aliases: [String], completion: @escaping (CXHandle) -> Void) {
		[...]
    }

}
#import "Provider.h"
#import "Contact.h"
#import "AddressBook.h"

#import <Bandyer/Bandyer.h>
#import <CallKit/CallKit.h>

@implementation Provider

- (instancetype)initWithAddressBook:(AddressBook *)addressBook
{
    self = [super init];

    if (self)
    {
        _addressBook = addressBook;
    }

    return self;
}

- (void)provideDetails:(NSArray<NSString *> *)aliases completion:(void (^)(NSArray<BDKUserDetails *> * _Nonnull))completion
{
    NSMutableArray<BDKUserDetails *> *details = [NSMutableArray arrayWithCapacity:aliases.count];

    for (NSString *alias in aliases)
    {
        Contact *contact = [self.addressBook contactForAlias:alias];
        BDKUserDetails *detail = [[BDKUserDetails alloc] initWithAlias:alias
                                                           firstname:contact.firstName
                                                            lastname:contact.lastName
                                                               email:contact.email
                                                            imageURL:contact.profileImageURL];
        [details addObject: detail];
    }

    completion(details);
}

- (void)provideHandle:(NSArray<NSString *> *)aliases completion:(void (^)(CXHandle * _Nonnull))completion
{
	[...]
}

@end

In the above snippet of code, we are pretending there's a class in our app named "AddressBook" which contains "Contact" information for any user. The class Provider conforms to the SDK protocol and creates an array of UserDetails getting the information from the "AddressBook" and then calls the completion block to signal the BandyerSDK it finished gathering the user information the SDK requested. In order for the BandyerSDK to use an instance of the "Provider" class we created, we must provide it to the userDetailsProvider property on the BandyerSDK singleton instance. Eventually, the final result looks like this:

custom-user-info-2

CallKit contact handles

As you might have noticed, the UserDetailsProvider requires you to implement two methods. The provideHandle(_:, completion:) method is needed by the SDK when CallKit support is enabled and the SDK needs to update the native system UI with user information. CallKit needs CXHandle objects to identify a contact inside the end-user address book. When an incoming or outgoing call is going to be processed by CallKit, the Bandyer SDK must provide those CXHandle objects to it. In order to create CXHandle objects with meaningful user information, the SDK must be provided an UserDetailsProvider object that is able to create CXHandle objects from "user aliases". The following snippets of code will show you how your class conforming to the UserDetailsProvider might create those CXHandle objects.

import Foundation
import CallKit
import Bandyer

class Provider: UserDetailsProvider {
    private let addressBook: AddressBook
    
    init(_ addressBook: AddressBook) {
        self.addressBook = addressBook
    }

    func provideDetails(_ aliases: [String], completion: @escaping ([UserDetails]) -> Void) {
		[...]
    }
    
    func provideHandle(_ aliases: [String], completion: @escaping (CXHandle) -> Void) {
        let contacts = addressBook.findContacts(aliases: aliases)
        let names = contacts.compactMap({$0.value.fullName})
        let handle = CXHandle(type: .generic, value: names.joined(separator: ", "))
        completion(handle)
    }

}
#import "Provider.h"
#import "Contact.h"
#import "AddressBook.h"

#import <Bandyer/Bandyer.h>
#import <CallKit/CallKit.h>

@implementation Provider

- (instancetype)initWithAddressBook:(AddressBook *)addressBook
{
    self = [super init];

    if (self)
    {
        _addressBook = addressBook;
    }

    return self;
}

- (void)provideDetails:(NSArray<NSString *> *)aliases completion:(void (^)(NSArray<BDKUserDetails *> * _Nonnull))completion
{
	[...]
}

- (void)provideHandle:(NSArray<NSString *> *)aliases completion:(void (^)(CXHandle * _Nonnull))completion
{	
    NSArray<Contact*> *contacts = [self.addressBook findContactsByAliases:aliases];
    NSMutableArray<NSString*> *names = [NSMutableArray array];
    for (Contact *contact in contacts) {
       NSString *name = [contact fullName];
       if (name != nil)
            [names addObject:name];
    }
    NSString *value = [names componentsJoinedByString:@", "];
    CXHandle *handle = [[CXHandle alloc] initWithType:CXHandleTypeGeneric value:value];
    completion(handle);
}

@end

Eventually, the final result should look like this:

CallKit handle

What's next

Clone this wiki locally