react-native-twilio-programmable-voice icon indicating copy to clipboard operation
react-native-twilio-programmable-voice copied to clipboard

Fix can not receive call from background when app was killed on IOS 14

Open vuletuanbt opened this issue 3 years ago • 1 comments

Hi! 👋

Firstly, thanks for your work on this project! 🙂

Today I used patch-package to patch [email protected] for the project I'm working on.

Here is the diff that solved my problem:

diff --git a/node_modules/react-native-twilio-programmable-voice/ios/RNTwilioVoice/RNTwilioVoice.h b/node_modules/react-native-twilio-programmable-voice/ios/RNTwilioVoice/RNTwilioVoice.h
index 117b1d6..9152068 100644
--- a/node_modules/react-native-twilio-programmable-voice/ios/RNTwilioVoice/RNTwilioVoice.h
+++ b/node_modules/react-native-twilio-programmable-voice/ios/RNTwilioVoice/RNTwilioVoice.h
@@ -7,5 +7,7 @@
 #import <React/RCTEventEmitter.h>
 
 @interface RNTwilioVoice : RCTEventEmitter <RCTBridgeModule>
-
+- (void) configCallKit: (NSDictionary *)params;
+- (void) reRegisterWithTwilioVoice;
+- (void) initPushRegistry;
 @end
diff --git a/node_modules/react-native-twilio-programmable-voice/ios/RNTwilioVoice/RNTwilioVoice.m b/node_modules/react-native-twilio-programmable-voice/ios/RNTwilioVoice/RNTwilioVoice.m
index 711ad26..7a21138 100644
--- a/node_modules/react-native-twilio-programmable-voice/ios/RNTwilioVoice/RNTwilioVoice.m
+++ b/node_modules/react-native-twilio-programmable-voice/ios/RNTwilioVoice/RNTwilioVoice.m
@@ -72,36 +72,7 @@ RCT_EXPORT_METHOD(initWithAccessToken:(NSString *)token) {
 }
 
 RCT_EXPORT_METHOD(configureCallKit: (NSDictionary *)params) {
-  if (self.callKitCallController == nil) {
-      /*
-       * The important thing to remember when providing a TVOAudioDevice is that the device must be set
-       * before performing any other actions with the SDK (such as connecting a Call, or accepting an incoming Call).
-       * In this case we've already initialized our own `TVODefaultAudioDevice` instance which we will now set.
-       */
-      self.audioDevice = [TVODefaultAudioDevice audioDevice];
-      TwilioVoice.audioDevice = self.audioDevice;
-
-      self.activeCallInvites = [NSMutableDictionary dictionary];
-      self.activeCalls = [NSMutableDictionary dictionary];
-
-    _settings = [[NSMutableDictionary alloc] initWithDictionary:params];
-    CXProviderConfiguration *configuration = [[CXProviderConfiguration alloc] initWithLocalizedName:params[@"appName"]];
-    configuration.maximumCallGroups = 1;
-    configuration.maximumCallsPerCallGroup = 1;
-    if (_settings[@"imageName"]) {
-      configuration.iconTemplateImageData = UIImagePNGRepresentation([UIImage imageNamed:_settings[@"imageName"]]);
-    }
-    if (_settings[@"ringtoneSound"]) {
-      configuration.ringtoneSound = _settings[@"ringtoneSound"];
-    }
-
-    _callKitProvider = [[CXProvider alloc] initWithConfiguration:configuration];
-    [_callKitProvider setDelegate:self queue:nil];
-
-    NSLog(@"CallKit Initialized");
-
-    self.callKitCallController = [[CXCallController alloc] init];
-  }
+    [self configCallKit:params];
 }
 
 RCT_EXPORT_METHOD(connect: (NSDictionary *)params) {
@@ -210,11 +181,77 @@ RCT_REMAP_METHOD(getCallInvite,
 }
 
 - (void)initPushRegistry {
   self.voipRegistry = [[PKPushRegistry alloc] initWithQueue:dispatch_get_main_queue()];
   self.voipRegistry.delegate = self;
   self.voipRegistry.desiredPushTypes = [NSSet setWithObject:PKPushTypeVoIP];
 }
 
+
+- (void) configCallKit: (NSDictionary *)params {
+    if (self.callKitCallController == nil) {
+        /*
+         * The important thing to remember when providing a TVOAudioDevice is that the device must be set
+         * before performing any other actions with the SDK (such as connecting a Call, or accepting an incoming Call).
+         * In this case we've already initialized our own `TVODefaultAudioDevice` instance which we will now set.
+         */
+        self.audioDevice = [TVODefaultAudioDevice audioDevice];
+        TwilioVoice.audioDevice = self.audioDevice;
+
+        self.activeCallInvites = [NSMutableDictionary dictionary];
+        self.activeCalls = [NSMutableDictionary dictionary];
+
+        _settings = [[NSMutableDictionary alloc] initWithDictionary:params];
+        CXProviderConfiguration *configuration = [[CXProviderConfiguration alloc] initWithLocalizedName:params[@"appName"]];
+        configuration.maximumCallGroups = 1;
+        configuration.maximumCallsPerCallGroup = 1;
+        if (_settings[@"imageName"]) {
+            configuration.iconTemplateImageData = UIImagePNGRepresentation([UIImage imageNamed:_settings[@"imageName"]]);
+        }
+        if (_settings[@"ringtoneSound"]) {
+            configuration.ringtoneSound = _settings[@"ringtoneSound"];
+        }
+
+        _callKitProvider = [[CXProvider alloc] initWithConfiguration:configuration];
+        [_callKitProvider setDelegate:self queue:nil];
+
+        NSLog(@"CallKit Initialized");
+
+        self.callKitCallController = [[CXCallController alloc] init];
+    }
+}
+
+- (void) reRegisterWithTwilioVoice {
+    NSString *accessToken = [self fetchAccessToken];
+    NSString *cachedDeviceToken = [[NSUserDefaults standardUserDefaults] objectForKey:kCachedDeviceToken];
+    NSLog(@"TwilioVoice accessToken: %@", [NSString stringWithFormat:@"%@",accessToken]);
+    NSLog(@"TwilioVoice cachedDeviceToken: %@", [NSString stringWithFormat:@"%@",cachedDeviceToken]);
+    if (cachedDeviceToken.length > 0) {
+        [TwilioVoice registerWithAccessToken:accessToken
+                                           deviceToken:cachedDeviceToken
+                                            completion:^(NSError *error) {
+                       if (error) {
+                           NSLog(@"An error occurred while re-registering: %@", [error localizedDescription]);
+                           NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
+                           [params setObject:[error localizedDescription] forKey:@"err"];
+
+                           [self sendEventWithName:@"deviceNotReady" body:params];
+                       }
+                       else {
+                           NSLog(@"Successfully re-registered for VoIP push notifications.");
+
+                           /*
+                            * Save the device token after successfully registered.
+                            */
+                           [[NSUserDefaults standardUserDefaults] setObject:cachedDeviceToken forKey:kCachedDeviceToken];
+                           [self sendEventWithName:@"deviceReady" body:nil];
+                       }
+                   }];
+                  
+    }
+    
+}
+

And then AppDelegate.m


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
 ...
  RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
  _rnTwilioVoice = [bridge moduleForClass:[RNTwilioVoice class]];
  [_rnTwilioVoice initPushRegistry];
  [_rnTwilioVoice reRegisterWithTwilioVoice];

    NSDictionary *configCallkit = @{@"appName": @"AppName"};
  [_rnTwilioVoice configCallKit:configCallkit];
  
  // ---  Voip Push Notification
  // ===== (THIS IS OPTIONAL BUT RECOMMENDED) =====
  // --- register VoipPushNotification here ASAP rather than in JS. Doing this from the JS side may be too slow for some use cases
  // --- see: https://github.com/react-native-webrtc/react-native-voip-push-notification/issues/59#issuecomment-691685841
  [RNVoipPushNotificationManager voipRegistration];
  ...
  return YES;
}

vuletuanbt avatar Mar 11 '21 06:03 vuletuanbt

@vuletuanbt Are you running into issues with maintaining an up-to-date push token with this whole cachedDeviceToken/accessToken thing?

I solved the issue by removing the if statement that compares them and just registering the token every time (which I believe is best practice per Apple's recommendations. Perhaps your solution addresses this issue?

danstepanov avatar Jul 05 '21 01:07 danstepanov