Skip to content

[iOS] [Bug report] isCallActive return undefined #278

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
ThinhVu opened this issue Sep 8, 2020 · 14 comments
Open

[iOS] [Bug report] isCallActive return undefined #278

ThinhVu opened this issue Sep 8, 2020 · 14 comments

Comments

@ThinhVu
Copy link

ThinhVu commented Sep 8, 2020

As docs in https://reactnative.dev/docs/native-modules-ios, ReactNative method always return void so to pass value to JS layer, we need to use callback, Promise or eventEmitter.

Current iOS implement. Return boolean value in this method doesn't send to JS layer

+ (BOOL)isCallActive:(NSString *)uuidString
{
    CXCallObserver *callObserver = [[CXCallObserver alloc] init];
    NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:uuidString];

    for(CXCall *call in callObserver.calls){
        NSLog(@"[RNCallKeep] isCallActive %@ %d ?", call.UUID, [call.UUID isEqual:uuid]);
        if([call.UUID isEqual:[[NSUUID alloc] initWithUUIDString:uuidString]] && !call.hasConnected){
            return true;
        }
    }
    return false;
}

Js index:

isCallActive = async(uuid) => await RNCallKeepModule.isCallActive(uuid);

I already tried with Promise method, it's work but not for my case.
I needed to call this method each second so I changed to event emitter to prevent "illegal callback invocation from native module" error.

PR is not available atm.

@ThinhVu ThinhVu changed the title [iOS] isCallActive return undefined [iOS] [Bug report] isCallActive return undefined Sep 9, 2020
@ThinhVu
Copy link
Author

ThinhVu commented Sep 9, 2020

Work-around for one who need this function.
In node_modules/react-native-callkeep/ios/RNCallKeep/RNCallKeep.m

  1. Define event emitter name
static NSString *const RNCallKeepDidLoadWithEvents = @"RNCallKeepDidLoadWithEvents";
static NSString *const RNCallKeepIsCallActive = @"RNCallKeepIsCallActive"; // <-- add this line
  1. Add defined event to supportedEvents array
- (NSArray<NSString *> *)supportedEvents
{
    return @[
        ...
        RNCallKeepDidLoadWithEvents,
        RNCallKeepIsCallActive. // <-- add this line
    ];
}
  1. Change exported method RCT_EXPORT_METHOD(isCallActive:(NSString *)uuidString) to
RCT_EXPORT_METHOD(isCallActive:(NSString *)uuidString)
{
#ifdef DEBUG
    NSLog(@"[RNCallKeep][isCallActive] uuid = %@", uuidString);
#endif
    CXCallObserver *callObserver = [[CXCallObserver alloc] init];
    NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:uuidString];

    for(CXCall *call in callObserver.calls){
        NSLog(@"[RNCallKeep] isCallActive %@ %d ?", call.UUID, [call.UUID isEqual:uuid]);
        if([call.UUID isEqual:[[NSUUID alloc] initWithUUIDString:uuidString]] && !call.hasConnected){
            [self sendEventWithNameWrapper:RNCallKeepIsCallActive body:@{ @"active": @true }];
            return;
        }
    }

    [self sendEventWithNameWrapper:RNCallKeepIsCallActive body:@{ @"active": @false }];
}
  1. In node_modules/react-native-callkeep/actions.js, add
const removeSubscription = (sub) => {
  eventEmitter.removeSubscription(sub)
}

and export it

export const listeners = {
  ...
  isCallActive,
  removeSubscription
};

  1. In node_modules/react-native-callkeep/index.js, change isCallActive = async(uuid) => await RNCallKeepModule.isCallActive(uuid); to
  isCallActive = uuid => {
    return new Promise((resolve, reject) => {
      try {
        const sub = listeners.isCallActive(({ active }) => {
          resolve(active)
          listeners.removeSubscription(sub)
        })
        RNCallKeepModule.isCallActive(uuid)
      } catch (e) {
        reject(e)
      }
    })
  }

Now you can using await RNCallKeep.isCallActive(uuid) in your code as expected.

@ajnozari
Copy link
Contributor

Iirc I made this contribution, I have a fix for this that I can submit as a PR this week. I have an exam tomorrow (Monday) but can look at submitting it after. Will fix this issue and expose it properly to the JS side.

At the time I made it I didn’t include the js layer since I interrupted it solely in native code.

@viveknair
Copy link

Hey there @ajnozari , did you get a chance to merge your PR for this?

@ajnozari
Copy link
Contributor

ajnozari commented Nov 2, 2020

I have worked on it but have not submitted another PR. I will get to it before the week is done.

@devWaleed
Copy link

devWaleed commented Dec 2, 2020

@ajnozari any updates with this? I am getting issues with this method as well, returns undefined as explained in #332

I tried the above solution, i don't get undefined now but it doesn't detect active call either, it rejects promise even though call is active in background

@ajnozari
Copy link
Contributor

@devWaleed I'm finally back at this part of our app and am giving this a look now. I'll have a pr coming soon.

@devWaleed
Copy link

@jitensuthar
Copy link

jitensuthar commented Jan 30, 2021

Hi @ajnozari, I could also use the isCallActive functionality to check against early/immediate acceptance of a call. Wondering if there is a PR still imminent!

@ajnozari
Copy link
Contributor

ajnozari commented Jan 30, 2021 via email

@ajnozari
Copy link
Contributor

ajnozari commented Jan 30, 2021 via email

@jitensuthar
Copy link

Appreciate the reply and detailed explanation @ajnozari. Out of curiousity, have you tried to answer or reject an incoming call very quickly, within a second of it coming in on iOS (i.e. the call comes in and you accept it nearly immediately on seeing it)? I may have something set up incorrectly or suboptimally so curious if you also experience the same behavior.

The accept and reject call handlers are NOT firing for me on iOS when I do this with the app in a kill state (not bg state, but killed). Nor are they captured through didLoadWithEvents. This is a very specific and perhaps not-too-common edge case, but I live for making sure all edge cases are cleanly handled :)

isCallActive seemed like the easiest fix for me to add in as a check on the JS side to at least handle the accepted call case. Obviously, determining call rejected won't be possible like this because I won't be able to distinguish between a rejected and unanswered call, but that I care less about (the call will just time out on the other side, same as if the callee ignored it or never picked up). Right now, CallKeep is not informing me of calls accepted in this (perhaps rare) scenario which is a problem I wanted to button up.

Again to clarify, all of this only applies for calls that were responded to near-immediately upon them displaying when the app was in a kill state. Otherwise, the handlers have been working great!

@devWaleed
Copy link

devWaleed commented Feb 1, 2021

Appreciate the reply and detailed explanation @ajnozari. Out of curiousity, have you tried to answer or reject an incoming call very quickly, within a second of it coming in on iOS (i.e. the call comes in and you accept it nearly immediately on seeing it)? I may have something set up incorrectly or suboptimally so curious if you also experience the same behavior.

The accept and reject call handlers are NOT firing for me on iOS when I do this with the app in a kill state (not bg state, but killed). Nor are they captured through didLoadWithEvents. This is a very specific and perhaps not-too-common edge case, but I live for making sure all edge cases are cleanly handled :)

isCallActive seemed like the easiest fix for me to add in as a check on the JS side to at least handle the accepted call case. Obviously, determining call rejected won't be possible like this because I won't be able to distinguish between a rejected and unanswered call, but that I care less about (the call will just time out on the other side, same as if the callee ignored it or never picked up). Right now, CallKeep is not informing me of calls accepted in this (perhaps rare) scenario which is a problem I wanted to button up.

Again to clarify, all of this only applies for calls that were responded to near-immediately upon them displaying when the app was in a kill state. Otherwise, the handlers have been working great!

I was using isCallActive in the same sense. If the call was accepted too early, didLoadWithEvents was not working correctly for me so I had only this method to verify if the call was accepted.

I use Firestore which tells me if a call was ended so that ends call on iOS Native UI by calling endCall function and passing UUID.

But I noticed one thing, if you're using debug build you may have to wait for at least 3 to 5 seconds before accepting call which will be notified as accept call event, if you do it before that it won't work. But in release mode I was able to pick call on iPhone 10 at around 1.5 seconds and it worked correctly. That was enough for me so I stopped looking for this method.

@ajnozari
Copy link
Contributor

ajnozari commented Feb 1, 2021

That is understandable, the delay from debug can be very deceiving sometimes.

@eminberkay
Copy link

the best is to find different library.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants