react-native-callkeep icon indicating copy to clipboard operation
react-native-callkeep copied to clipboard

didLoadWithEvents always getting RNCallKeepDidDisplayIncomingCall

Open devWaleed opened this issue 3 years ago • 60 comments

Bug report

  • [x] I've checked the example to reproduce the issue.

  • Reproduced on:

  • [x] 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

devWaleed avatar Nov 26 '20 10:11 devWaleed

Just updated my app to latest version of VoipPushNotifications and I am able to receive multiple events of VOIP package correctly but not for CallKeep

@manuquentin sir i'll really appreciate your help here. Any comment would be very useful for us.

devWaleed avatar Dec 04 '20 11:12 devWaleed

Hi, just wanna ask in ios , i send a voip notification. Do i need to build the app/archived or is it working during development?

Im expecting to open/relaunch the app from background/killed?

Thoughts on this please. Thank you

mikeangelsilva avatar Dec 06 '20 19:12 mikeangelsilva

@mikeangelsilva Hey, if you're using APN with VOIP type make sure you're using correct URLs for sending notification i.e for development build you will use development certificate and sendbox URLs, and when you upload to Testflight you'll use production certificates and live URLs of APN service.

devWaleed avatar Dec 06 '20 20:12 devWaleed

Just updated my app to latest version of VoipPushNotifications and I am able to receive multiple events of VOIP package correctly but not for CallKeep

@manuquentin sir i'll really appreciate your help here. Any comment would be very useful for us.

Hi I am having a similar issue. The VOIP works fine, but Callkeep does not. In my case, even didLoadWithEvents is not called. Did you find any solution to this? Thanks for your help!

weijihuang avatar Jan 08 '21 00:01 weijihuang

Same here any luck with solving that? it is just reporting: RNCallKeepDidDisplayIncomingCall even if user tap accept or reject.

m4r00p avatar Jan 12 '21 14:01 m4r00p

RNCallKeepDidDisplayIncomingCall even if user tap accept or reject.

Can you put some log to see how many RNCallKeepDidDisplayIncomingCall event was triggered? If it's one event for each call, then it should be correct

namnm avatar Jan 13 '21 08:01 namnm

Yes, it is a single RNCallKeepDidDisplayIncomingCall for each call which is correct. But incorrect or missing behavior is NOT calling a certain listener answer/end or adding those events in didLoadWithEvents array with js was not ready.

I have the same setup and behavior as the bug report describes above.

m4r00p avatar Jan 13 '21 12:01 m4r00p

RNCallKeepDidDisplayIncomingCall even if user tap accept or reject.

Can you put some log to see how many RNCallKeepDidDisplayIncomingCall event was triggered? If it's one event for each call, then it should be correct

Also, I tested that the answerCall in Obj-C somehow did not get triggered. I have no idea what is wrong.

weijihuang avatar Jan 15 '21 06:01 weijihuang

Strange that Im using the latest and it likely never happens. Im curious that how did you register the handlers, in hook, or didmount, or directly in the bundle?

namnm avatar Jan 15 '21 12:01 namnm

I have them directly in the index.js like this:

` RNCallKeep.addEventListener('answerCall', onAnswerCallAction); RNCallKeep.addEventListener('endCall', onEndCallAction); RNCallKeep.addEventListener('didDisplayIncomingCall', onIncomingCallDisplayed);

`

And also I was debugging Obj-C and answerCall was not called for me. Exactly same as for @Stephen-HWJ

m4r00p avatar Jan 19 '21 14:01 m4r00p

Any luck with reproducing the issue? Are you testing on iOS 14.3?

m4r00p avatar Jan 25 '21 10:01 m4r00p

Any luck with reproducing the issue? Are you testing on iOS 14.3?

Not really. I am testing on 14.3

weijihuang avatar Jan 27 '21 07:01 weijihuang

@Stephen-HWJ and you are getting the correct user action event in didLoadWithEvents besides just RNCallKeepDidDisplayIncomingCall? Can you paste or share the code you are using to test?

m4r00p avatar Jan 27 '21 22:01 m4r00p

@Stephen-HWJ Would you mind posting your setup so we can push the issue forward? It is kind of holding us down and I believe more people may experience this issue.

m4r00p avatar Feb 05 '21 13:02 m4r00p

BTW. Guys @namnm @devWaleed any luck with solving this issue?

m4r00p avatar Feb 10 '21 09:02 m4r00p

@Stephen-HWJ Would you mind posting your setup so we can push the issue forward? It is kind of holding us down and I believe more people may experience this issue.

Please see the code:

    RNCallKeep.addEventListener(
      'didLoadWithEvents',
      async (events) => {    
        const validEvents = compact(events);
        let callDataToAdd = null;
        let callDataToAnswer = null;
        let callDataToReject = null;
        let callDataToInitiateCall = null;
        each(compact(validEvents), event => {
          const { name, data } = event;
          if (name === 'RNCallKeepDidDisplayIncomingCall') {
            callDataToAdd = data;
          }
          if (name === 'RNCallKeepPerformAnswerCallAction') {
            callDataToAnswer = data;
          }
          if (name === 'RNCallKeepPerformEndCallAction') {
            callDataToReject = data;
          }
        });

        const lastEventName = get(last(validEvents), 'name');
        if (lastEventName === 'RNCallKeepDidReceiveStartCallAction') {
          callDataToInitiateCall = get(last(validEvents), 'data');
          // callDataToAnswer = null;
          if (!callDataToReject) {
            callDataToAdd = null;
          }
        }
        if (lastEventName === 'RNCallKeepPerformAnswerCallAction') {
          callDataToAnswer = get(last(validEvents), 'data');
        }

        if (callDataToAdd) {
          didDisplayIncomingCallEvent(callDataToAdd);
          if (callDataToReject) {
            cancelCallHandler(callDataToReject);
          }
          if (callDataToAnswer) {
            answerCallEvent(callDataToAnswer);
          }
        }
        if (callDataToInitiateCall) {
          // this.onStartCall(callDataToAnswer);
        }
      });

weijihuang avatar Feb 15 '21 07:02 weijihuang

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

Jerome91410 avatar Feb 16 '21 16:02 Jerome91410

@devWaleed could you check with the PR if your issue is fixed? thx

Jerome91410 avatar Feb 17 '21 21:02 Jerome91410

@m4r00p Im trying the above PR My forked repo: https://github.com/brekekesoftware/react-native-callkeep

namnm avatar Feb 21 '21 08:02 namnm

@Jerome91410 @namnm I've just patched this file locally. Yes, the @Jerome91410's fix is addressing this issue perfectly.

Great thanks to all you guys!!!

m4r00p avatar Mar 03 '21 15:03 m4r00p

@Jerome91410 @namnm I've just patched this file locally. Yes, the @Jerome91410's fix is addressing this issue perfectly.

Great thanks to all you guys!!!

Can you explain how to do this?

stephanoparaskeva avatar Mar 05 '21 15:03 stephanoparaskeva

Our application is blocked by this bug.

stephanoparaskeva avatar Mar 05 '21 15:03 stephanoparaskeva

@stephanoparaskeva In your package.json you have something like this:

  "dependencies": {
    ...
    "react-native-callkeep": "^4.0.1",
    ...
  }

Then change it to

  "dependencies": {
    ...
    "react-native-callkeep": "github:Jerome91410/react-native-callkeep#fix-init",
    ...
  }

After that try remove the node_module for sure, npm install (or yarn install), then rerun pod install and rerun the project in your XCode.

namnm avatar Mar 05 '21 16:03 namnm

@devWaleed could you check with the PR if your issue is fixed? thx

This did not fix the issue for me, using this PR:

    "react-native-callkeep": "github:react-native-webrtc/react-native-callkeep#pull/365/head",

Sometimes I see RNCallKeepDidPerfrormAnswerCallAction

Sometimes I do not.

stephanoparaskeva avatar Mar 05 '21 20:03 stephanoparaskeva

"react-native-callkeep": "github:Jerome91410/react-native-callkeep#fix-init",

@Jerome91410

This did not work either 😔

It works on iPhone 6 but not iPhone 12

EDIT: it sometimes does not work on my iphone 6, it probably works 5-10% of the time.

stephanoparaskeva avatar Mar 05 '21 20:03 stephanoparaskeva

@stephanoparaskeva After some more test, I can confirm your issue, although Im using the fork it still happens. Not sure how can we fix this?

namnm avatar Mar 11 '21 03:03 namnm

@namnm @stephanoparaskeva

I do not reproduce anymore the issue with this pr, but checked only on iphone 8 and XR.

Maybe you can provide logs about callkeep and a reproduce method ?

Don't know if there is any impact but i report with the completion handler defined

[RNCallKeep reportNewIncomingCall: uuid
                              handle: phonenumber
                          handleType: @"generic"
                            hasVideo: hasVideo
                 localizedCallerName: fullname
                     supportsHolding: YES
                        supportsDTMF: YES
                    supportsGrouping: NO
                  supportsUngrouping: NO
                         fromPushKit: YES
                             payload: data
               withCompletionHandler: completion];

Jerome91410 avatar Mar 11 '21 08:03 Jerome91410

@Jerome91410 Just tested on iphone6, still happens. Tried again on iphoneXs but can not reproduce. May be because newer phones have more processing power and load the js bridge faster? Then the code may be still having issue.

namnm avatar Mar 11 '21 11:03 namnm

Have tried with on both IPhone 6 and IPhone 12 Pro and it does not work:

Have tried this with versions:

    "react-native-callkeep": "github:brekekesoftware/react-native-callkeep",
    "react-native-callkeep": "github:Jerome91410/react-native-callkeep#fix-init",
    "react-native-voip-push-notification": "^3.1.0",

stephanoparaskeva avatar Mar 11 '21 15:03 stephanoparaskeva

@Jerome91410 What logs can I provide you with that'd help you debug my issue, how do I retrieve these logs?

Here is my appdelegate.m

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

// --- 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
- (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"];
  // @"%@ (Connecting...)"
  NSString *callerName = [NSString stringWithFormat:@"%@", payload.dictionaryPayload[@"callerName"]];
  NSString *handle = payload.dictionaryPayload[@"handle"];
  
  // --- 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 >4.0.0
  [RNCallKeep reportNewIncomingCall: uuid
                             handle: handle
                         handleType: @"generic"
                           hasVideo: YES
                localizedCallerName: callerName
                    supportsHolding: YES
                       supportsDTMF: YES
                   supportsGrouping: YES
                 supportsUngrouping: YES
                        fromPushKit: YES
                            payload: payload.dictionaryPayload
              withCompletionHandler: completion];
}

My code:

  useEffect(() => {
    RNCallKeep.setup(SETUP_OPTIONS);
    registerDeviceForNotifications();
    new Sound('ringback.mp3', Sound.MAIN_BUNDLE);

    RNCallKeep.setAvailable(true);
    RNCallKeep.addEventListener('didDisplayIncomingCall', incoming =>
      dispatch(setIncomingCall(incoming))
    );
    RNCallKeep.addEventListener('didLoadWithEvents', didLoadWithEvents);
    RNCallKeep.addEventListener('answerCall', accept);

    return () => {
      RNCallKeep.removeEventListener('didDisplayIncomingCall');
      RNCallKeep.removeEventListener('didLoadWithEvents');
      RNCallKeep.removeEventListener('answerCall');
    };
  }, []);

Reproduction steps:

  1. Call phone when app is in background/terminated.
  2. Accept call.
  3. didLoadWithEvents only contains RNCallKeepDidDisplayIncomingCall

What do you think @zxcpoiu ?

stephanoparaskeva avatar Mar 14 '21 06:03 stephanoparaskeva