Start an audio and video call

Updated

Shows how to implement real-time audio and video communications using the AgoraChatCallKit.

AgoraChatCallKit is an open-source audio and video UI library developed based on Agora's real-time communications and signaling services. With this library, you can implement audio and video calling functionalities with enhanced synchronization between multiple devices. In use-cases where a user ID is logged in to multiple devices, once the user deals with an incoming call that is ringing on one device, all the other devices stop ringing simultaneously.

This page describes how to implement real-time audio and video communications using the AgoraChatCallKit.

Understand the tech

The basic process for implementing real-time audio and video communications with AgoraChatCallKit is as follows:

  1. Initialize AgoraChatCallKit by calling init.
  2. Call startSingleCallWithUid or startInviteUsers on the caller's client to send a call invitation for one-to-one or group calls.
  3. On the callee's client, accept or decline the call invitation after receiving callDidReceive. Once a user accepts the invitation, they enter the call.
  4. When the call ends, the SDK triggers the callDidEnd callback.

Prerequisites

Before proceeding, ensure that your development environment meets the following requirements:

  • Xcode 9.0 or later.
  • A device running iOS 10.0 or later.
  • A Chat project that has integrated the Chat SDK and implemented the basic real-time chat functionalities, including users logging in and out and sending and receiving messages.

Project setup

Take the following steps to integrate the AgoraChatCallKit into your project and set up the development environment.

Add the AgoraChatCallKit framework

AgoraChatCallKit is developed upon Agora_Chat_iOS, Masonry, AgoraRtcEngine_iOS/RtcBasic, and SDKWebImage. Refer to the following steps to import AgoraChatCallKit into your project. If you prefer to add the framework manually, refer to Manually import AgoraChatCallKit.

  1. Install CocoaPods if you have not. For details, see Getting Started with CocoaPods.

  2. If your project does not have a Podfile file, navigate to the project root directory, and run the pod init command to create a text file Podfile in the project folder.

  3. Drag AgoraChatCallKit.framework to the project and set the Embed attribute of AgoraChatCallKit.framework as Embed & Sign under Frameworks, Libraries, and Embedded Content.

     use_frameworks!
     // Replace AppName with the name of your project
     target 'AppName' do
         pod 'AgoraChatCallKit'
         // Other frameworks
     end
  4. In the project root directory, run pod install to add the framework. When the SDK is installed successfully, you can see Pod installation complete! in the Terminal and an xcworkspace file in the project folder.

  5. Open the xcworkspace file in Xcode.

Add permissions

Open the info.plist file, and add the following permissions:

KeyTypeValue
Privacy - Microphone Usage DescriptionStringThe description for using the microphone. For example, to access the microphone.
Privacy - Camera Usage DescriptionStringThe description for using the camera. For example, to access the camera.

Implement audio and video calling

This section introduces the core steps for implementing audio and video calls in your project.

Initialize AgoraChatCallKit

Call init to initialize the AgoraChatCallKit.

AgoraChatCallConfig *config = [[AgoraChatCallConfig alloc] init];
config.agoraAppId = @"agoraAppId";
config.enableRTCTokenValidate = YES;
config.enableIosCallKit = YES;
[AgoraChatCallManager.sharedManager initWithConfig:config delegate:self];

In this method, you need to set the AgoraChatCallConfig interface. Some of the configurations include the following:

@interface AgoraChatCallConfig : NSObject
/**
 * The timeout duration for the call invitation, in seconds. The default value is 30.
 */
@property (nonatomic) UInt32 callTimeOut;
/**
 * The dictionary of the user information. The data format is key-value pairs, where key represents the user ID and value is EaseCallUser.
 */
@property (nonatomic,strong) NSMutableDictionary* users;
/**
 * Specify the file URL of the ringtone. This field is valid only when enableIosCallKit is set to NO.
 */
@property (nonatomic,strong) NSURL* ringFileUrl;
/**
 * The App ID of the project, which you can obtain from Agora Console.
 */
@property (nonatomic,strong) NSString* agoraAppId;
/**
 *  Whether to enable token authentication when users join the Agora channel:
 *  - (Default) YES: Enable token authentication. Once you set it as YES, you must implement the callDidRequestRTCTokenForAPPId callback. After receiving this callback, you need to call setRTCToken to pass in the token to start or join a call.
 *  - NO: Do not enable token authentication.
 */
@property (nonatomic) BOOL enableRTCTokenValidate;
/**
 * Whether to use the iOS CallKit framework.
 */
@property (nonatomic, assign) BOOL enableIosCallKit;

@end

Send a call invitation

From the caller's client, call startSingleCallWithUId or startInviteUsers to send a call invitation for a one-to-one call or group call. You need to specify the call type when calling the method.

  • One-to-one audio call

    [AgoraChatCallManager.sharedManager startSingleCallWithUId:userId type:AgoraChatCallType1v1Audio ext:nil completion:^(NSString * callId, AgoraChatCallError * aError) {
    }];
  • One-to-one video call

    [AgoraChatCallManager.sharedManager startSingleCallWithUId:userId type:AgoraChatCallType1v1Video ext:nil completion:^(NSString * callId, AgoraChatCallError * aError) {
    }];
  • Group audio call

    ConfInviteUsersViewController *controller = [[ConfInviteUsersViewController alloc] initWithGroupId:groupId excludeUsers:@[AgoraChatClient.sharedClient.currentUsername]];
    controller.didSelectedUserList = ^(NSArray * _Nonnull aInviteUsers) {
        for (NSString *strId in aInviteUsers) {
            AgoraChatUserInfo *info = [UserInfoStore.sharedInstance getUserInfoById:strId];
            if (info && (info.avatarUrl.length > 0 || info.nickname > 0)) {
                    AgoraChatCallUser *user = [AgoraChatCallUser userWithNickName:info.nickname image:[NSURL URLWithString:info.avatarUrl]];
                [[AgoraChatCallManager.sharedManager getAgoraChatCallConfig] setUser:strId info:user];
            }
        }
        [AgoraChatCallManager.sharedManager startInviteUsers:aInviteUsers groupId:groupId callType:AgoraChatCallTypeMultiAudio ext:@{
            @"groupId":groupId
        } completion:^(NSString * callId, AgoraChatCallError * aError) {
    
        }];
    };
    
    controller.modalPresentationStyle = UIModalPresentationPageSheet;
    [viewController presentViewController:controller animated:YES completion:nil];
  • Group video call

    ConfInviteUsersViewController *controller = [[ConfInviteUsersViewController alloc] initWithGroupId:groupId excludeUsers:@[AgoraChatClient.sharedClient.currentUsername]];
    controller.didSelectedUserList = ^(NSArray * _Nonnull aInviteUsers) {
        for (NSString *strId in aInviteUsers) {
            AgoraChatUserInfo *info = [UserInfoStore.sharedInstance getUserInfoById:strId];
            if (info && (info.avatarUrl.length > 0 || info.nickname > 0)) {
                    AgoraChatCallUser *user = [AgoraChatCallUser userWithNickName:info.nickname image:[NSURL URLWithString:info.avatarUrl]];
                [[AgoraChatCallManager.sharedManager getAgoraChatCallConfig] setUser:strId info:user];
            }
        }
        [AgoraChatCallManager.sharedManager startInviteUsers:aInviteUsers groupId:groupId callType:AgoraChatCallTypeMultiVideo ext:@{
            @"groupId":groupId
        } completion:^(NSString * callId, AgoraChatCallError * aError) {
    
        }];
    };
    
    controller.modalPresentationStyle = UIModalPresentationPageSheet;
    [viewController presentViewController:controller animated:YES completion:nil];

The following screenshot gives an example of the user interface after sending a call invitation for one-to-one audio call:

Receive the invitation

Once a call invitaion is sent, the callee receives the invitation in the callDidReceive callback.

- (void)callDidReceive:(EaseCallType)aType inviter:(NSString*_Nonnull)user ext:(NSDictionary*)aExt
{

}

If the callee is online and available for a call, you can pop out a user interface that allows the callee to accept or decline the invitation. If you have enabled the iOS CallKit, the system call user interface is launched. Otherwise, you can refer to the following screenshot to implement the interface:

Send a call invitation during a group call

In call sessions with multiple users, these users can also send call invitations to other users. After sending the invitation, the SDK triggers the multiCallDidInvitingWithCurVC callback in AgoraChatCallDelegate on the sender's client.

/**
 * Occurs when the local user sends a call invitation during a group call.
 * vc     The current UI view controller.
 * users  The user IDs that are already in the group call.
 * aExt   Extension information.
 */
- (void)multiCallDidInvitingWithCurVC:(UIViewController*_Nonnull)vc excludeUsers:(NSArray *_Nullable)users ext:(NSDictionary *)aExt
  {
    NSString *groupId = nil;
    if (aExt) {
        groupId = [aExt objectForKey:@"groupId"];
    }
    if (!groupId || groupId.length  0 || info.nickname.length > 0)) {
                AgoraChatCallUser *user = [AgoraChatCallUser userWithNickName:info.nickname image:[NSURL URLWithString:info.avatarUrl]];
                [[AgoraChatCallManager.sharedManager getAgoraChatCallConfig] setUser:strId info:user];
            }
        }
        [AgoraChatCallManager.sharedManager startInviteUsers:aInviteUsers groupId:groupId callType:AgoraChatCallTypeMultiAudio ext:aExt completion:nil];
    };
    confVC.modalPresentationStyle = UIModalPresentationPopover;
    [vc presentViewController:confVC animated:YES completion:nil];
}

Listen for callback events

During the call, you can also listen for the following callback events:

  • callDidJoinChannel, occurs when the local user successfully joins the call.

    - (void)callDidJoinChannel:(NSString*_Nonnull)aChannelName uid:(NSUInteger)aUid
    {
        [self _fetchUserMapsFromServer:aChannelName];
    }
  • remoteuserDidJoinChannel, occurs when the remote user successfully joins the call.

    -(void)remoteUserDidJoinChannel:( NSString*_Nonnull)aChannelName uid:(NSInteger)aUid username:(NSString*_Nullable)aUserName
    {
        if (aUserName.length > 0) {
            AgoraChatUserInfo* userInfo = [[UserInfoStore sharedInstance] getUserInfoById:aUserName];
            if (userInfo && (userInfo.avatarUrl.length > 0 || userInfo.nickname.length > 0)) {
                AgoraChatCallUser* user = [AgoraChatCallUser userWithNickName:userInfo.nickname image:[NSURL URLWithString:userInfo.avatarUrl]];
                [[AgoraChatCallManager.sharedManager getAgoraChatCallConfig] setUser:aUserName info:user];
            }
        } else {
            [self _fetchUserMapsFromServer:aChannelName];
        }
    }

When receiving the callDidJoinChannel:uid or remoteUserDidJoinChannel:uid:username: callback, the user needs to look up the Chat user ID corresponding to the Agora UID in your app server. If the Chat user ID is found, construct a dictionary with Agora UID and Chat user ID and then set it to the app using setUsers:channelName:.

End the call

A one-to-one call ends as soon as one of the two users hangs up, while a group call ends only after the local user hangs up. When the call ends, the SDK triggers the callDidEnd callback.

- (void)callDidEnd:(NSString*_Nonnull)aChannelName reason:(AgoraChatCallEndReason)aReason time:(int)aTm type:(AgoraChatCallType)aType
{
    NSString *msg = @"";
    switch (aReason) {
        case AgoraChatCallEndReasonHandleOnOtherDevice:
            msg = NSLocalizedString(@"otherDevice", nil);
            break;
        case AgoraChatCallEndReasonBusy:
            msg = NSLocalizedString(@"remoteBusy", nil);
            break;
        case AgoraChatCallEndReasonRefuse:
            msg = NSLocalizedString(@"RemoteRefuseCall", nil);
            break;
        case AgoraChatCallEndReasonCancel:
            msg = NSLocalizedString(@"cancelCall", nil);
            break;
        case AgoraChatCallEndReasonNoResponse:
            msg = NSLocalizedString(@"remoteNoResponse", nil);
            break;
        case AgoraChatCallEndReasonHangup:
            msg = [NSString stringWithFormat:NSLocalizedString(@"callendPrompt", nil),aTm];
            break;
        default:
            break;
    }
    if (msg.length > 0) {
        [self showHint:msg];
    }
}

Next steps

This section contains extra steps you can take for the audio and video call functionalities in your project.

Call exceptions

If exceptions or errors occur during a call, the SDK triggers the callDidOccurError callback, which reports the detailed information of the exception in AgoraChatCallError.

- (void)callDidOccurError:(AgoraChatCallError *_Nonnull)aError
{
}

Update the user avatar or nickname

After a user joins the call, you can call setUser to modify the avatar and nickname of the current user and the other users in the channel.

AgoraChatCallUser *user = [AgoraChatCallUser userWithNickName:info.nickname image:[NSURL URLWithString:info.avatarUrl]];
[[AgoraChatCallManager.sharedManager getAgoraChatCallConfig] setUser:userId info:user];

Authenticate users with the RTC token

To enhance communication security, Agora recommends that you authenticate app users with the RTC token before they join a call. To do this, you need to make sure that the Primary Certificate of your project is enabled, and enableRTCTokenValidate in the AgoraChatCallKit is set to YES.

config.enableRTCTokenValidate = YES;
[AgoraChatCallManager.sharedManager initWithConfig:config delegate:self];

Once you enable token authentication, ensure that you listen for the callDidRequestRTCTokenForAppId callback. When callDidRequestRTCTokenForAppId is triggered, you need to retrieve the token and call setRTCToken to pass the token to the CallKit. Tokens are generated on your app server using the token generator provided by Agora. For how to generate a token on the server and retrieve and renew the token on the client, see Secure authentication with tokens.

- (void)callDidRequestRTCTokenForAppId:(NSString *)aAppId channelName:(NSString *)aChannelName account:(NSString *)aUserAccount uid:(NSInteger)aAgoraUid
{
    [AgoraChatCallManager.sharedManager setRTCToken:rtcToken channelName:aChannelName uid: The Agora UID];
}

Reference

This section provides other reference information that you can refer to when implementing real-time audio and video communications functionalities.

API list

The AgoraChatCallKit framework contains the AgoraChatCallManager and AgoraChatCallDelegate classes.

The following table lists the core methods in AgoraChatCallManager:

MethodDescription
initWithConfig
Initializes AgoraChatCallKit.
startSingleCallWithUId:type:ext
Starts a one-to-one call.
startInviteUsers:groupId:callType:ext:completion:Starts a group call.
getAgoraChatCallConfigRetrieves the configurations of AgoraChatCallKit.
setRTCToken:channelName:uid:Sets the RTC Token.
setUsers:channelName:Sets the mapping between Chat user ID and Agora user ID (UID).

The following table lists the callbacks in AgoraChatCallDelegate:

EventDescription
callDidEnd:reason:time:type:Occurs when the call ends.
multiCallDidInvitingWithCurVC:callType:excludeUsers:ext:Occurs when a member of the group call invites other users to the call.
callDidReceive:inviter:ext:Occurs when the call invitation is received and the device rings.
callDidRequestRTCTokenForAppId:channelName:account:uid:Requests the RTC token.
callDidOccurError:Reports exceptions and errors during the call.
remoteUserDidJoinChannel:uid:username:Occurs when a remote user joins the call.
callDidJoinChannel:uid:Occurs when the current user joins the call.

Sample project

Agora provides an open-source AgoraChat-ios sample project on GitHub. You can download the sample to try it out or view the source code.

The sample project uses the Chat user ID to join a channel, which enables displaying the user ID in the view of the call. If you use the methods of the Agora to start a call, you can also use the Integer UID to join a channel.

Import AgoraChatCallKit manually

Refer to the following steps to manually add AgoraChatCallKit to your project:

  1. Download AgoraChatCallKit, and extract the downloaded file.
  2. Copy and paste AgoraChatCallKit.framework to the directory of your project.
  3. In Xcode, go to TARGETS > Project Name > General. Drag AgoraChatCallKit.framework under Frameworks, Libraries, and Embedded Content, and set the Embed attribute of AgoraChatCallKit.framework as Embed & Sign.

Other project settings

You can configure the following project settings according to your use case:

  • To run the app in the background, go to info.plist, click +, and add Required background modes. Set Type as Array, and add App plays audio or streams audio/video using AirPlay as a value under Required background modes.
  • To use the Apple PushKit and CallKit, go to TARGETS > Project Name > Signing and Capabilities. Under Background Modes, check Voice over IP.