question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

didLoadWithEvents always getting RNCallKeepDidDisplayIncomingCall

See original GitHub issue

Bug report

  • I’ve checked the example to reproduce the issue.

  • Reproduced on:

  • iOS

Description

I have tried to register didLoadWithEvents in index.js and as well as App.js which is my first component. Both are returning RNCallKeepDidDisplayIncomingCall even if i press accept button right when callkit ui appears, i don’t get acll accept event name here and if i press accept, i don’t get answercall event either

Steps to Reproduce

Versions

- Callkeep: 4.0
- React Native: 0.63.3
- iOS: 14
- Phone model: iPhone 7 

Logs

[Thu Nov 26 2020 15:51:10.950] LOG didLoadWithEvents -> events {“data”: {“callUUID”: “f7f3f338-4c65-416a-893e-522ce1f3de68”, “error”: “”, “fromPushKit”: “1”, “handle”: “Task: Adnana”, “hasVideo”: “1”, “localizedCallerName”: “Adnan guild2 (Connecting…)”, “payload”: {“35video”: true, “53video”: true, “callId”: “rymTxN91PDVWQIcCOp8q”, “callUUID”: “f7f3f338-4c65-416a-893e-522ce1f3de68”, “callerAvatar”: “”, “callerId”: 53, “callerName”: “Adnan guild2”, “channelId”: “…”, “recieverId”: 35, “status”: “incomingCall”, “taskId”: 711, “title”: “Adnana”, “type”: “video”}, “supportsDTMF”: “0”, “supportsGrouping”: “0”, “supportsHolding”: “0”, “supportsUngrouping”: “0”}, “name”: “RNCallKeepDidDisplayIncomingCall”}

#import "AppDelegate.h"

#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import <Firebase.h>
#import "RNFirebaseNotifications.h"
#import "RNFirebaseMessaging.h"
#import "RNSplashScreen.h"

#import "RNCallKeep.h"

//VOIP
#import <PushKit/PushKit.h>                    /* <------ add this line */
#import "RNVoipPushNotificationManager.h"      /* <------ add this line */

#ifdef FB_SONARKIT_ENABLED
#import <FlipperKit/FlipperClient.h>
#import <FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h>
#import <FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h>
#import <FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.h>
#import <SKIOSNetworkPlugin/SKIOSNetworkAdapter.h>
#import <FlipperKitReactPlugin/FlipperKitReactPlugin.h>

//VOIP

static void InitializeFlipper(UIApplication *application) {
  FlipperClient *client = [FlipperClient sharedClient];
  SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults];
  [client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]];
  [client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]];
  [client addPlugin:[FlipperKitReactPlugin new]];
  [client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]];
  [client start];
}
#endif

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  [FIRApp configure];
  [RNFirebaseNotifications configure];
#ifdef FB_SONARKIT_ENABLED
  InitializeFlipper(application);
#endif
  
  

  RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
  
  RNVoipPushNotificationManager* voipModule = [bridge moduleForClass:[RNVoipPushNotificationManager class]];
  [voipModule voipRegistration];
  
  
  RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
                                                   moduleName:@"Artisan"
                                            initialProperties:nil];

  rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];

  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
  UIViewController *rootViewController = [UIViewController new];
  rootViewController.view = rootView;
  self.window.rootViewController = rootViewController;
  [self.window makeKeyAndVisible];
  [RNSplashScreen show]; 
  return YES;
}

- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
  [[RNFirebaseNotifications instance] didReceiveLocalNotification:notification];
}

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo
                                                       fetchCompletionHandler:(nonnull void (^)(UIBackgroundFetchResult))completionHandler{
  [[RNFirebaseNotifications instance] didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
}

- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings {
  [[RNFirebaseMessaging instance] didRegisterUserNotificationSettings:notificationSettings];
}

- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
  return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
#else
  return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
}



 - (BOOL)application:(UIApplication *)application
 continueUserActivity:(NSUserActivity *)userActivity
   restorationHandler:(void(^)(NSArray * __nullable restorableObjects))restorationHandler
 {
   return [RNCallKeep application:application
            continueUserActivity:userActivity
              restorationHandler:restorationHandler];
 }


//VOIP
/* Add PushKit delegate method */

// --- Handle updated push credentials
- (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(PKPushType)type {
  // Register VoIP push token (a property of PKPushCredentials) with server
  [RNVoipPushNotificationManager didUpdatePushCredentials:credentials forType:(NSString *)type];
}

- (void)pushRegistry:(PKPushRegistry *)registry didInvalidatePushTokenForType:(PKPushType)type
{
  // --- The system calls this method when a previously provided push token is no longer valid for use. No action is necessary on your part to reregister the push type. Instead, use this method to notify your server not to send push notifications using the matching push token.
}


// --- Handle incoming pushes (for ios <= 10)
- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(PKPushType)type {
  [RNVoipPushNotificationManager didReceiveIncomingPushWithPayload:payload forType:(NSString *)type];
}

// --- Handle incoming pushes (for ios >= 11)
- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(PKPushType)type withCompletionHandler:(void (^)(void))completion {
  

  // --- NOTE: apple forced us to invoke callkit ASAP when we receive voip push
  // --- see: react-native-callkeep

  // --- Retrieve information from your voip push payload
  NSString *uuid = payload.dictionaryPayload[@"uuid"];
  NSString *callerName = [NSString stringWithFormat:@"%@ (Connecting...)", payload.dictionaryPayload[@"callerName"]];
  NSString *handle = payload.dictionaryPayload[@"handle"];
  NSString *callData = payload.dictionaryPayload[@"callData"];
  

  // --- this is optional, only required if you want to call `completion()` on the js side
  [RNVoipPushNotificationManager addCompletionHandler:uuid completionHandler:completion];

  // --- Process the received push
  [RNVoipPushNotificationManager didReceiveIncomingPushWithPayload:payload forType:(NSString *)type];

  // --- You should make sure to report to callkit BEFORE execute `completion()`
//  [RNCallKeep reportNewIncomingCall:uuid
//                             handle:handle
//                         handleType:@"generic"
//                           hasVideo:true
//                localizedCallerName:callerName
//                        fromPushKit: YES
//                            payload:callData];
//
  [RNCallKeep reportNewIncomingCall: uuid
                 handle: handle
             handleType: @"generic"
               hasVideo: true
    localizedCallerName: callerName
        supportsHolding: false
           supportsDTMF: false
       supportsGrouping: false
     supportsUngrouping: false
            fromPushKit: YES
                payload: callData
  withCompletionHandler: nil];
  // --- You don't need to call it if you stored `completion()` and will call it on the js side.
  completion();
}
//VOIP
@end

Issue Analytics

  • State:open
  • Created 3 years ago
  • Reactions:2
  • Comments:60 (5 by maintainers)

github_iconTop GitHub Comments

8reactions
Jerome91410commented, Feb 16, 2021

Same issue. didLoadWithEvents only contains RNCallKeepDidDisplayIncomingCall.

Checked into Objective C, it seems that the CXAnswerCallAction is not called.

It only appears when I receive a push notif and the app is killed. There is a race somewhere, if i accept the call before the js loaded, then the AnswerCall is not reached.

- Callkeep: 4.0.1
- React Native: 0.61.5
- iOS: 14.4
- Phone model: iPhone 8

Reproduction method:

  1. be sure that your app is killed
  2. send a push voip notif to your app
  3. accept the call before js was fully loaded
  4. The js never receive the RNCallKeepPerformAnswerCallAction, even in didLoadWithEvents

When the issue is reproduced:

  • i checked the call’s value of hasConnected from callObserver:
    • hasEnded is equals to false
    • hasConnected is equals to false
  • verified all events received by the module from the beginning , and what i remarked is sendEventWithNameWrapper never received the RNCallKeepPerformAnswerCallAction event

If i wait that js is fully loaded, then the event is correctly received

2reactions
Romick2005commented, Mar 15, 2021

Based on @Jerome91410 fork I created my own https://github.com/Romick2005/react-native-callkeep.

What problem I solved:

  1. Init CallKeep on native site, not on JS.
  2. Pass a function that will called at any call reported event. And make them public via native interface like: RNCallKeepPerformEndCallAction, RNCallKeepDidDisplayIncomingCall, … etc

AppDelegate:

-(BOOL) application: (UIApplication *) application didFinishLaunchingWithOptions: (NSDictionary *) launchOptions
{
#ifdef FB_SONARKIT_ENABLED
  InitializeFlipper(application);
#endif

  RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate: self launchOptions: launchOptions];

  RCTRootView *rootView = [[RCTRootView alloc] initWithBridge: bridge moduleName: @"{appName}" initialProperties: nil];

  rootView.backgroundColor = [[UIColor alloc] initWithRed: 1.0f green: 1.0f blue: 1.0f alpha: 1];

  [[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error: nil];

  self.window = [[UIWindow alloc] initWithFrame: [UIScreen mainScreen].bounds];
  UIViewController *rootViewController = [UIViewController new];
  rootViewController.view = rootView;
  self.window.rootViewController = rootViewController;
  [self.window makeKeyAndVisible];

  [[FBSDKApplicationDelegate sharedInstance] application: application
    didFinishLaunchingWithOptions: launchOptions];

  // Callkeep setup
  // We have to set appName as settings for CallKeep in standardUserDefaults, because we may not call RNCallKeep.setup(options) on JS side
  NSDictionary *settings = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"RNCallKeepSettings"];

  NSBundle *bundle = [NSBundle mainBundle];
  NSDictionary *info = [bundle infoDictionary];

  settings = @{
    @"appName": [info objectForKey:@"CFBundleDisplayName"]
  };

  RNCallKeep *callKeep = [RNCallKeep allocWithZone: nil];
  [callKeep initCallKitProvider: settings

    withEventHandler: ^(NSString *eventName, id data) {
     
      NSLog (@"[RNCallKeep][setup] event eventName = %@, data = %@", eventName, data);

      if (eventName == RNCallKeepHandleStartCallNotification) {
        NSLog (@"[RNCallKeep][withEventHandler] RNCallKeepHandleStartCallNotification");
      } else if (eventName == RNCallKeepDidReceiveStartCallAction) {
        NSLog (@"[RNCallKeep][withEventHandler] RNCallKeepDidReceiveStartCallAction");
      } else if (eventName == RNCallKeepPerformAnswerCallAction) {
        NSLog (@"[RNCallKeep][withEventHandler] RNCallKeepPerformAnswerCallAction");
      } else if (eventName == RNCallKeepPerformEndCallAction) {

        // Prepare data for http request
        NSString *callUUID = data[@"callUUID"];
        NSString *cancelUrl = "{http://server/call/end}"; // TODO set your server url
        NSDictionary *parameters = @{
          @"callUUID": callUUID,
          @"reasonId": @6
        };

        // Send http delete request to reject call
        NSData *callEndData = [NSJSONSerialization dataWithJSONObject: parameters options: NSJSONWritingPrettyPrinted error: nil];
        NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
        [request setURL: [NSURL URLWithString: cancelUrl]];
        [request setHTTPMethod: @"DELETE"];
        [request addValue: @"application/json" forHTTPHeaderField: @"Content-Type"];
        NSString *postLength = [NSString stringWithFormat: @"%lu", [callEndData length]];
        [request setValue: postLength forHTTPHeaderField: @"Content-Length"];
        [request setHTTPBody: callEndData];
        [[[NSURLSession sharedSession] dataTaskWithRequest: request] resume];
        [callUUIDCallDataMap removeObjectForKey: callUUID];

        NSLog (@"[RNCallKeep][withEventHandler] RNCallKeepPerformEndCallAction");

        [NSThread sleepForTimeInterval:2.0f];
      } else if (eventName == RNCallKeepDidActivateAudioSession) {
        NSLog (@"[RNCallKeep][withEventHandler] RNCallKeepDidActivateAudioSession");
      } else if (eventName == RNCallKeepDidDeactivateAudioSession) {
        NSLog (@"[RNCallKeep][withEventHandler] RNCallKeepDidDeactivateAudioSession");
      } else if (eventName == RNCallKeepDidDisplayIncomingCall) {
        NSString *callUUID = data[@"callUUID"];
        [callUUIDCallDataMap setObject: data forKey: callUUID];

        NSLog (@"[RNCallKeep][withEventHandler] RNCallKeepDidDisplayIncomingCall");
      } else if (eventName == RNCallKeepDidPerformSetMutedCallAction) {
        NSLog (@"[RNCallKeep][withEventHandler] RNCallKeepDidPerformSetMutedCallAction");
      } else if (eventName == RNCallKeepPerformPlayDTMFCallAction) {
        NSLog (@"[RNCallKeep][withEventHandler] RNCallKeepPerformPlayDTMFCallAction");
      } else if (eventName == RNCallKeepDidToggleHoldAction) {
        NSLog (@"[RNCallKeep][withEventHandler] RNCallKeepDidToggleHoldAction");
      } else if (eventName == RNCallKeepProviderReset) {
        NSLog (@"[RNCallKeep][withEventHandler] RNCallKeepProviderReset");
      } else if (eventName == RNCallKeepCheckReachability) {
        NSLog (@"[RNCallKeep][withEventHandler] RNCallKeepCheckReachability");
      } else if (eventName == RNCallKeepDidLoadWithEvents) {
        NSLog (@"[RNCallKeep][withEventHandler] RNCallKeepDidLoadWithEvents");
      } else {
        NSLog (@"[RNCallKeep][withEventHandler] Event %@not found.", eventName);
      }
    }
  ];

  return YES;
}

@stephanoparaskeva can you please check if my approach work for you?

Read more comments on GitHub >

github_iconTop Results From Across the Web

wazo-pbx - Bountysource
I have implimented react-native-callkeep 4.3.3. when I get call It will show me call screen where I can receive or reject call.
Read more >
react-native-callkeep - npm
Set selfManaged: true in setup. On an incoming call, from react native, call RNCallKeep. displayIncomingCall. CallKeep will then fire the ...
Read more >
For me RNCallKeep.addEventListener('answerCall') never ...
... but for some reason when call UI pops up and I accept the call, no event is firing. I have written my...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found