Search code examples
iosobjective-ccallkit

CXProviderDelegate Methods not called


My goal is to open my CallViewController after the user answers the call. I've read some excerpts here on SO that it can be done using:

- (void)provider:(CXProvider *)provider performAnswerCallAction:(CXAnswerCallAction *)action {
    NSLog(@"performAnswerCallAction");
}

- (void)provider:(CXProvider *)provider performStartCallAction:(CXStartCallAction *)action {
     NSLog(@"performStartCallAction");
}

The code above is inside AppDelegate.

However, using NSLogs, these methods aren't triggered at all. I can't seem to find any tutorials on implementing using Objective-C that is easy to understand from a beginner's POV. I would appreciate any insights, thanks!


Solution

  • First of all, did you set the delegate of CXProvider?

    let provider = CXProvider(configuration: your_call_kit_config)
    provider.setDelegate(self, queue: nil) // 'nil' means it will run on main queue
    

    Also, have the AppDelegate.swift file conform to CXProviderDelegate

    provider:performAnswerCallAction: gets called when user taps on 'Answer' button on the incoming call screen provided by the system. (Apple documentation HERE).

    provider:performStartCallAction: gets called after successful request to CXCallController to perform a CXStartCallAction (Apple documentation HERE).

    EDIT: For Objective-C

    Ok here is the Objective-C snippet, for example, in the AppDelegate. First, you need to make AppDelegate conform to the CXProviderDelegate, Like so:

    @interface AppDelegate () <CXProviderDelegate>
    

    Then, add a property for CXProvider and CXCallController, like so:

    @interface AppDelegate () <CXProviderDelegate>
    
    @property (nonatomic, nonnull, strong) CXProvider *provider;
    @property (nonatomic, nonnull, strong) CXCallController *callController;
    
    @end
    

    In the AppDelegate's function application:willFinishLaunchingWithOptions:, initialize CXProvider object with the call configuration, like this:

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
        // Setup CallKit
        CXProviderConfiguration *providerConfiguration = [[CXProviderConfiguration alloc] initWithLocalizedName:@"MyApp"];
        providerConfiguration.supportsVideo = YES;
        providerConfiguration.includesCallsInRecents = YES;
        self.provider = [[CXProvider alloc] initWithConfiguration: providerConfiguration];
    
        // Since `AppDelegate` conforms to `CXProviderDelegate`, set it to the provider object
        // Setting 'nil' to `queue` argument means, that the methods will be executed on main thread.
        // Optionally, you can assign private serial queue to handle `CXProvider` method responses
        [self.provider setDelegate:self queue:nil];
    
        // Initialize `CallController`
        self.callController = [[CXCallController alloc] init];
    
        return YES;
    

    }

    And on the bottom of AppDelegate.m file, implement CXProviderDelegate methods:

    #pragma mark: - CXProvider delegate methods
    
    - (void)providerDidReset:(CXProvider *)provider {
        // Drop all calls here (if there are pending)
    }
    
    /*!
     This method gets called when user presses on 'Accept' button on the incoming call screen provided by the system.
     Here you should configure `AVAudioSession` for VoIP calls and handle logic for answering the call.
     */
    - (void)provider:(CXProvider *)provider performAnswerCallAction:(CXAnswerCallAction *)action {
        // Put your answering logic here
        // Note: It is important to fulfill the action inside the scope of it's function, or to fail, depending if error occured during answering
        [action fulfill];
    }
    
    /*!
     This method gets called when CXCallController object finishes the CXStartCallAction request. You need to request
     start call action to the CXCallController instance, when starting an outgoing VoIP call. After successful transaction,
     the provider will respond with this delegate method. Here you should also configure `AVAudioSession` for VoIP calls.
     */
    - (void)provider:(CXProvider *)provider performStartCallAction:(CXStartCallAction *)action {
        // Put your outgoing call action here
        // Note: It is important to fulfill the action inside the scope of it's function, or to fail, depending if error occured during starting a call
        [action fulfill];
    }
    

    To start an outgoing VoIP call, you need to request the transaction with the action to the CXCallController instance, like so:

    #pragma mark - Call Controller requests
    
    - (void)startOutgoingVoIPCallWithNumber:(NSString *)number {
        NSUUID *callUUID = [NSUUID UUID]; // Here you create or assign UUID of call.
        CXHandle *callHandle = [[CXHandle alloc] initWithType:CXHandleTypePhoneNumber value:number];
        CXStartCallAction *startCallAction = [[CXStartCallAction alloc] initWithCallUUID:callUUID handle:callHandle];
    startCallAction.video = YES; // Yes or no is call is video or audio.
    
        CXTransaction *startCallTransaction = [[CXTransaction alloc] initWithAction:startCallAction];
    
        [self.callController requestTransaction:startCallTransaction completion:^(NSError * _Nullable error) {
            if (error) {
                // Handle start call error here
                // Ususally, error occurs if the system cannot handle the new outgoing call, since there are others pending
            }
            // If there is no error, CXProvider will respond with `provider:performStartCallAction:` method.
        }];
    }
    

    To display system call screen

    #pragma mark - Report New incoming call
    
    /*!
     You need to call this function each time you receive a new incoming call, usually right from the VoIP push notification.
     */
    - (void)reportNewIncomingCallWithNumber:(NSString *)number {
        NSUUID *callUUID = [NSUUID UUID]; // Call UUID, you should have this in some Call object, not generating the new here
        CXHandle *callHandle = [[CXHandle alloc] initWithType:CXHandleTypePhoneNumber value:number];
        CXCallUpdate *callInfo = [[CXCallUpdate alloc] init];
        callInfo.remoteHandle = callHandle;
        [self.provider reportNewIncomingCallWithUUID:[NSUUID UUID] update:callInfo completion:^(NSError * _Nullable error) {
            if (error) {
                // Handle error here
            }
            // If there is no error, system will display incoming call screen and when user taps on 'Answer',
            // `CXProvider` will respond with `provider:performAnswerCallAction:`
        }];
    }