Closed zxcpoiu closed 3 years ago
We have 2 API:
And 3 Events:
...
import VoipPushNotification from 'react-native-voip-push-notification';
...
class MyComponent extends React.Component {
...
// --- or anywhere which is most comfortable and appropriate for you, usually ASAP
componentDidMount() {
VoipPushNotification.registerVoipToken(); // --- register token
VoipPushNotification.addEventListener('didLoadWithEvents', (events) => {
// --- this will fire when there are events occured before js bridge initialized
// --- use this event to execute your event handler manually by event type
if (!events || !Array.isArray(events) || events.length < 1) {
return;
}
for (let voipPushEvent of events) {
let { name, data } = voipPushEvent;
if (name === VoipPushNotification.RNVoipPushRemoteNotificationsRegisteredEvent) {
this.onVoipPushNotificationRegistered(data);
} else if (name === VoipPushNotification.RNVoipPushRemoteNotificationReceivedEvent) {
this.onVoipPushNotificationiReceived(data);
}
}
});
// --- onVoipPushNotificationRegistered
VoipPushNotification.addEventListener('register', (token) => {
// --- send token to your apn provider server
});
// --- onVoipPushNotificationiReceived
VoipPushNotification.addEventListener('notification', (notification) => {
// --- when receive remote voip push, register your VoIP client, show local notification ... etc
this.doSomething();
// --- optionally, if you `addCompletionHandler` from the native side, once you have done the js jobs to initiate a call, call `completion()`
VoipPushNotification.onVoipNotificationCompleted(notification.getData().uuid);
});
}
// --- unsubscribe event listeners
componentWillUnmount() {
VoipPushNotification.removeEventListener('didLoadWithEvents');
VoipPushNotification.removeEventListener('register');
VoipPushNotification.removeEventListener('notification');
}
...
}
The PushKit require us to REGISTER delegate first, then the PushKit obj is responsible to pass credential
and voip push
to our delegate.
Without register delegate, we will not receive any callback from PushKit, so it is recommended to register voip ASAP in AppDelegate.m
I have exposed voipRegistration
as a static method and a lock _isVoipRegisterd = YES / NO
checks to prevent duplicated delegate register.
readme updated as well.
Usage to register voip push in AppDelegate.m ASAP thanks to @chevonc pointed this out in https://github.com/react-native-webrtc/react-native-voip-push-notification/issues/59#issuecomment-691685841
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
// ===== (THIS IS OPTIONAL) =====
// --- 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];
// ===== (THIS IS OPTIONAL) =====
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"AppName" initialProperties:nil];
}
I will leave it here few days for folks to test and feedback. Then merge into master with 3.0 version.
@zxcpoiu
I tested with the optional [RNVoipPushNotificationManager voipRegistration]
in appDelegate.m
.
Sound good, CallKeep was called even the app was killed or in background.
Just 2 remarks, don't know if it's the expected behavior:
didLoadWithEvents
with token, I received it only a the first start, after an app restart (force kill) it is not reached.didLoadWithEvents
with RNVoipPushRemoteNotificationReceivedEvent
env: ios: 14 rn: 0.61.5
thanks
HI @Jerome91410 , thanks for the feedback.
didLoadWithEvents
is more like a helper function which let you can get the events fired BEFORE you have subscribed event listener.
So it does NOT mean the didLoadWithEvents
will fire each time the app started, instead, it only fires IF there are events occurred in the native too early and BEFORE JS bridge is initialized.
If the JS bridge is initialized earlier, the event will fires through the normal event handler.
subscribe VoipPush events ASAP, like in your app.js
or your main component's constructor
or at the global scope ( outside of your main component ), register events in componentDidMount may be too late sometime. ( but it will work with didLoadWithEvents
eventually though. )
I personally subscribe didLoadWithEvents
first, then subscribe the rest.
Each event should fire once, either via didLoadWithEvents
or normal register
/ notification
, and you should subscribe them ASAP and handle all the event listeners.
Thanks for the explanation.
It's exactly what I did. The package is initialized in app.js
at the really beginning of the app.
I'll double check, but as the app is started (previously killed) due to a push voip received, the event should be automatically fired before the JS initialization.
@zxcpoiu I doubled check.
You was right, the listener initialization of didLoadWithEvents
takes some time to be called. I tried to improve my code, but was able to listen the events at least 600ms after the JS initialization.
I did some changes; and now I am more confident to get the token (if requested from native part) and the previous notification with this kind of code:
diff --git a/node_modules/react-native-voip-push-notification/index.js b/node_modules/react-native-voip-push-notification/index.js
index 88949f1..c5f730b 100644
--- a/node_modules/react-native-voip-push-notification/index.js
+++ b/node_modules/react-native-voip-push-notification/index.js
@@ -95,6 +95,31 @@ export default class RNVoipPushNotification {
RNVoipPushNotificationManager.registerVoipToken();
}
+ /**
+ * usefull to get the last register token if your JS takes time to initialize the listener on `didLoadWithEvents`
+ * Should be call before events initialization
+ *
+ * @static
+ * @memberof RNVoipPushNotification
+ *
+ *
+ */
+ static getLastVoipToken() {
+ return RNVoipPushNotificationManager.getLastVoipToken();
+ }
+
+ /**
+ * usefull to get the last notification if your JS takes time to initialize the listener on `didLoadWithEvents`
+ * Should be call before events initialization
+ *
+ * @static
+ * @memberof RNVoipPushNotification
+ *
+ */
+ static getLastNotification() {
+ return RNVoipPushNotificationManager.getLastNotification();
+ }
+
/**
* When you have processed necessary initialization for voip push, tell ios completed.
* This is mainly for ios 11+, which apple required us to execute `complete()` when we finished.
diff --git a/node_modules/react-native-voip-push-notification/ios/RNVoipPushNotification/RNVoipPushNotificationManager.m b/node_modules/react-native-voip-push-notification/ios/RNVoipPushNotification/RNVoipPushNotificationManager.m
index 2699a77..4ee9130 100644
--- a/node_modules/react-native-voip-push-notification/ios/RNVoipPushNotification/RNVoipPushNotificationManager.m
+++ b/node_modules/react-native-voip-push-notification/ios/RNVoipPushNotification/RNVoipPushNotificationManager.m
@@ -30,7 +30,8 @@ @implementation RNVoipPushNotificationManager
static bool _isVoipRegistered = NO;
static NSMutableDictionary<NSString *, RNVoipPushNotificationCompletion> *completionHandlers = nil;
-
+static NSString *_lastVoipToken = nil;
+static NSDictionary *_lastNotification = nil;
// =====
// ===== RN Module Configure and Override =====
@@ -160,9 +161,10 @@ + (void)didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(NSStr
for (NSUInteger i = 0; i < voipTokenLength; i++) {
[hexString appendFormat:@"%02x", bytes[i]];
}
-
RNVoipPushNotificationManager *voipPushManager = [RNVoipPushNotificationManager allocWithZone: nil];
[voipPushManager sendEventWithNameWrapper:RNVoipPushRemoteNotificationsRegisteredEvent body:[hexString copy]];
+ _lastVoipToken =[hexString copy];
+
}
// --- should be called from `AppDelegate.didReceiveIncomingPushWithPayload`
@@ -171,9 +173,9 @@ + (void)didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(NSSt
#ifdef DEBUG
RCTLog(@"[RNVoipPushNotificationManager] didReceiveIncomingPushWithPayload payload.dictionaryPayload = %@, type = %@", payload.dictionaryPayload, type);
#endif
-
RNVoipPushNotificationManager *voipPushManager = [RNVoipPushNotificationManager allocWithZone: nil];
[voipPushManager sendEventWithNameWrapper:RNVoipPushRemoteNotificationReceivedEvent body:payload.dictionaryPayload];
+ _lastNotification =payload.dictionaryPayload;
}
// --- getter for completionHandlers
@@ -212,6 +214,28 @@ + (void)removeCompletionHandler:(NSString *)uuid
});
}
+
+// --- get last voip push token
+RCT_REMAP_METHOD(getLastVoipToken,
+ resolverToken:(RCTPromiseResolveBlock)resolve
+ rejecterToken:(RCTPromiseRejectBlock)reject)
+{
+#ifdef DEBUG
+ RCTLog(@"[RNVoipPushNotificationManager] getLastVoipToken() _lastVoipToken = %@", _lastVoipToken);
+#endif
+ resolve(_lastVoipToken);
+ _lastVoipToken = nil;
+}
+
+// --- get last voip push token
+RCT_REMAP_METHOD(getLastNotification,
+ resolverNotif:(RCTPromiseResolveBlock)resolve
+ rejecterNotif:(RCTPromiseRejectBlock)reject)
+{
+ resolve(_lastNotification);
+ _lastNotification = nil;
+}
+
// --- called from js when finished to process incoming voip push
RCT_EXPORT_METHOD(onVoipNotificationCompleted:(NSString *)uuid)
{
how i use it:
const lastNotification = await VoipPushNotification.getLastNotification();
if (lastNotification) {
... // Do what you want with
}
VoipPushNotification.addEventListener('didLoadWithEvents', events => {
...
})
VoipPushNotification.addEventListener('notification', notification => {
...
})
Thanks @Jerome91410
Yeh, I got your point and have considered it while I made this PR. This is more like a confidence level to the js event bridge.
The principle of this lib is to propagate native events to the js reliably and brainlessly, said, even if it is a duplicated register event from PushKit or when multiple VoipPush received at the same time, we should propagate it to the js for user to handle.
Honestly, I personally like the reliable feel of your solution ( invoke and get response right away instead waiting events without any indicator ), but it can't handle the multiple voip notification arrived at the same time since you only have on variable to store it.
Another potential race condition ( or duplicate events confusion ) of your solution may occur like below:
const lastNotification = await VoipPushNotification.getLastNotification();
if (lastNotification) {
... // Do what you want with
}
// =====
// Race Condition and risk for duplicated events here
//
// after we check the initial event, whether it is exist or not,
// at this moment, we received another event,
// but while native `startObserving` is not being called yet and `_hasListeners` is still `NO`,
//
// We will not only save the last event to `_lastNotification` / `_lastVoipToken` but also add it to the cached `_delayedEvents`.
//
// So eventually, we still relied on `didLoadWithEvents`
// or we can call `getLastNotification()` / `getLastVoipToken()`
// again after we've subscribed event listeners. ( But we won't
// know when `startObserving` is actually being called in js side
// until we got the first event )
//
// And we have to check / distinguish whether the events from `didLoadWithEvents` are duplicated with `getLastNotification` / `getLastVoipToken`
//
// To harmonize the above issue, we will increase complexity to this lib or our app
//
// =====
VoipPushNotification.addEventListener('didLoadWithEvents', events => {
...
})
So if your use case is really that time sensitive which can't wait for the event bridge, you can made another PR on top of this one to add the method, I am happy to merge it but would not recommend it in the doc.
Thanks for sharing it. :+1:
@zxcpoiu .. We tested this branch with iOS and everything is working flawlessly on our end. Specifically, it has successfully fixed VOIP notifications showing while the app is in background/killed states and successfully fires the 'notification' callback so we can register our websocket.
Thank you for your contribution!
Does this allow both Android + IOS open app when terminated, or just IOS?
Is it possible for Android too?
Also how do you register that the incoming call has hanged up?
E.g: Call user -> user phone is off -> cancel call -> user turns phone on -> user opens app -> incoming call (already been cancelled on other side). How do you fix this?
@stephanoparaskeva
Does this allow both Android + IOS open app when terminated, or just IOS?
Is it possible for Android too?
This lib is for iOS only, entirely nothing to do with android.
Also how do you register that the incoming call has hanged up?
E.g: Call user -> user phone is off -> cancel call -> user turns phone on -> user opens app -> incoming call (already been cancelled on other side). How do you fix this?
There is nothing to fix but how your app use Voip Push
For cancel a expired voip push, usually:
@zxcpoiu
Hi for some reason my registration callback has stopped firing. I have tried with both
[RNVoipPushNotificationManager voipRegistration];
and
RNVoipPushNotificationManager.registerVoipToken();
My version is:
"react-native-voip-push-notification": "github:react-native-webrtc/react-native-voip-push-notification#pull/69/head",
Here's some logs:
It was working before, but now 'runs 3' never gets console.logged:
console.warn('runs');
if (Platform.OS === 'ios') {
console.warn('runs2');
// VoipPushNotification.registerVoipToken();
VoipPushNotification.addEventListener('register', (token: string) => {
console.warn('runs3');
PushNotification.configure({
onRegister: onRegister(token),
onNotification,
popInitialNotification: true,
requestPermissions: true,
alert: true,
badge: true,
sound: true,
} as any);
});
@stephanoparaskeva
You can do a simple experiment:
register
event listener firstVoipPushNotification.registerVoipToken()
If you see the register event, then the issue you mentioned is likely a event fired before bridge start
You should both check: didLoadWithEvents
and your normal register
events. That's the issue that this PR is trying to solve.
@zxcpoiu
When I press the button I don't get 'register' callback. But this This is what I get in Xcode Console when I press the button:
So 'register' event never runs in my JS code, and I don't know what the APNS_VOIP token is
AppDelegate.m for reference. AppDelegate.h for reference.
Also, thank you for helping and this library. It was amazing when it worked for me. I'm just not sure why it's stopped.
These are some logs of when the app first loads:
You have called [RNVoipPushNotificationManager voipRegistration]
In your AppDelegate.m
line 54. So when you call again in JS side, the log says: already registered
, this is to prevent duplicate registered delegate for PushKit. ( https://github.com/react-native-webrtc/react-native-voip-push-notification/pull/69#issuecomment-729590045 )
You should use BOTH: didLoadWithEvents and your normal register events.
Please check this example out:
https://github.com/react-native-webrtc/react-native-voip-push-notification/pull/69#issuecomment-729224144 and echo log inside didLoadWithEvents
You have called
[RNVoipPushNotificationManager voipRegistration]
In yourAppDelegate.m
line 54. So when you call again in JS side, the log says:already registered
, this is to prevent duplicate registered delegate for PushKit. ( #69 (comment) )You should use BOTH: didLoadWithEvents and your normal register events.
Please check this example out: #69 (comment) and echo log inside
didLoadWithEvents
This worked, thank you!
@zxcpoiu, I like your way of solving voip push notifications - clean and nice. I have only one question/observation there are didLoadWithEvents event that are used to fire delayed push notification. Would it have no consequences to remove it, but when we will have js listeners added, we can fire only “register” or “notification” events from the delayed events queue. Is there any specific reason that we would need didLoadWithEvents? Because I think it is a good idea to hide unnecessary event for the end user and simplify the lib setup. What do you think?
@Romick2005
First, this solution is originally provided from @konradkierus at https://github.com/react-native-webrtc/react-native-callkeep/pull/169, there are some discussion similar your question https://github.com/react-native-webrtc/react-native-callkeep/pull/169#issuecomment-598882577
My thinking path when I'm writing this PR was simply:
register
and notification
event arrived order is matter? ( it is matter in callkeep based on the discussion )
I'm not sure if some people, for example, use register
and notification
on different places and different time.
Personally, I prefer to hide didLoadWithEvent
and just fired cached event when specific listener had subscribed ( like you mentioned ), but from the unknown use cases
point of view, I choose to align with callkeep's api and let user to decide it.
I don't see any contradiction with @konradkierus. When we will remove didLoadWithEvents, we will keep the order of events based on the index in queue. So that will also quarantie the order and we would have full control over events.
May be I didn't understand what is your point.
Do you mean We don't need didLoadWithEvents entirely
right?
on native side, track each event on it's own queue, when the event subscribed, fire and flush the corresponding queue. If user does not subscribe for a event, every event fired in native side would stored into a quere?
Or use one queue, when specific event subscribed, for each the queue, fire and flush for the specific event?
Is above what you mean?
Yep, I suggest to remove didLoadWithEvents entirely as it is unnecessary and simplifies the code. I would stick with two queues and fire only after subscribe to specific event and in order to have last event probably we can emit even specific queue as an array, where q[q.length-1] would be the latest event. What do you think of having event timestamp?
Yep, I suggest to remove didLoadWithEvents entirely as it is unnecessary and simplifies the code. I would stick with two queues and fire only after subscribe to specific event and in order to have last event probably we can emit even specific queue as an array, where q[q.length-1] would be the latest event. What do you think of having event timestamp?
Disagree entirely. I use 'didLoadWithEvents' And I don't even need 'register'. Register event gets called in Native code and my app never sees event as it always runs first (in AppDelegate.m). So my app Always Loads with event using 'didLoadWithEvent' and this is used to set the APNS VOIP token to async storage and the app then uses token to send to VOIP Backend to find and call phone.
'didLoadWithEvents' is very useful.
@stephanoparaskeva Do you know that didLoadWithEvents fire both register and notification events in one array? If you don't need 'register' than you don't have to subscribe to it. Probably you do not get the idea here. So you will just have to listen 'register' in your app and when get one - use received voip token. That would be possible only after suggested changes. And just remove unneeded 'didLoadWithEvent'. By the way how do you handle voip token change event?
@stephanoparaskeva Do you know that didLoadWithEvents fire both register and notification events in one array? If you don't need 'register' than you don't have to subscribe to it. Probably you do not get the idea here. So you will just have to listen 'register' in your app and when get one - use received voip token. That would be possible only after suggested changes. And just remove unneeded 'didLoadWithEvent'. By the way how do you handle voip token change event?
I honestly think it's fine as is. 'didLoadWithEvents' gives you all events executed before JS code runs, so you can have events first before everything else. It's quite clever.
By the way how do you handle voip token change event?
I don't handle this event, should I, and what for?
Hey, sorry for late reply, and happy new year.
Actually @Romick2005 is right, we can of course remove didLoadWithEvents
entirely, and cache all events separately
in separate native event queue then fires them when corresponding event listener registered from js. This is more intuitive, but the cons are what I mentioned above:
didLoadWithEvent
api on CallKeep
libs.So I prefer to not introduce another breaking change again, unless it's necessary and the change can compatible with both method ( ex: while implementing multiple separated queue for more intuitive api like @Romick2005 said and support didLoadWithEvents
as well for user which require the exact event order )
addEventListener
@zxcpoiu Please should this be used alongside the native configurations in the AppDelegate.m
. I have been trying to get this library to receive voip notiifcation and then call react native callkeep but it does not work.
below is my code:
AppDelegate.m
#import "AppDelegate.h"
#import "RNCallKeep.h"
#import <UserNotifications/UserNotifications.h>
#import <RNCPushNotificationIOS.h>
#import <PushKit/PushKit.h>
#import "RNVoipPushNotificationManager.h"
#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import <CodePush/CodePush.h>
#import <UMCore/UMModuleRegistry.h>
#import <UMReactNativeAdapter/UMNativeModulesProxy.h>
#import <UMReactNativeAdapter/UMModuleRegistryAdapter.h>
#import <React/RCTLinkingManager.h>
@interface AppDelegate () <RCTBridgeDelegate>
@property (nonatomic, strong) UMModuleRegistryAdapter *moduleRegistryAdapter;
@end
@implementation AppDelegate
// Required for deeplinking
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
return [RCTLinkingManager application:application openURL:url options:options];
}
// Required for the register event.
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
[RNCPushNotificationIOS didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}
// Required for the notification event. You must call the completion handler after handling the remote notification.
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
[RNCPushNotificationIOS didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
}
// Required for the registrationError event.
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
[RNCPushNotificationIOS didFailToRegisterForRemoteNotificationsWithError:error];
}
// Required for localNotification event
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
withCompletionHandler:(void (^)(void))completionHandler
{
[RNCPushNotificationIOS didReceiveNotificationResponse:response];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.moduleRegistryAdapter = [[UMModuleRegistryAdapter alloc] initWithModuleRegistryProvider:[[UMModuleRegistryProvider alloc] init]];
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
// ===== (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];
// ===== (THIS IS OPTIONAL BUT RECOMMENDED) =====
[RNCallKeep setup:@{
@"appName": @"SmartHealthMobile",
@"maximumCallGroups": @3,
@"maximumCallsPerCallGroup": @1,
@"supportsVideo": @NO,
}];
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
moduleName:@"SmartHealthMobile"
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];
// Define UNUserNotificationCenter
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
center.delegate = self;
[super application:application didFinishLaunchingWithOptions:launchOptions];
return YES;
}
/* 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
- (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 *uuid = @"44713e33-fa78-4ff5-8ec5-983e0832d1c6";
NSString *callerName = @"Test";
NSString *handle = @"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 reportNewIncomingCall: uuid
handle: handle
handleType: @"generic"
hasVideo: NO
localizedCallerName: callerName
supportsHolding: YES
supportsDTMF: YES
supportsGrouping: YES
supportsUngrouping: YES
fromPushKit: YES
payload: nil
withCompletionHandler: completion];
// --- You don't need to call it if you stored `completion()` and will call it on the js side.
completion();
}
- (NSArray<id<RCTBridgeModule>> *)extraModulesForBridge:(RCTBridge *)bridge
{
NSArray<id<RCTBridgeModule>> *extraModules = [_moduleRegistryAdapter extraModulesForBridge:bridge];
// If you'd like to export some custom RCTBridgeModules that are not Expo modules, add them here!
return extraModules;
}
//Called when a notification is delivered to a foreground app.
-(void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
{
completionHandler(UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge);
}
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
#else
return [CodePush bundleURL];
#endif
}
- (BOOL)application:(UIApplication *)application
continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:(void(^)(NSArray * __nullable restorableObjects))restorationHandler
{
return [RNCallKeep application:application
continueUserActivity:userActivity
restorationHandler:restorationHandler];
}
@end
then is my class that exports voip listeners
export class VoipNotificationInterface {
constructor() {
VoipPushNotification.registerVoipToken()
this.setupEventListeners()
}
static instance = null
static getInstance() {
if (this.instance == null) this.instance = new VoipNotificationInterface()
return this.instance
}
setupEventListeners = () => {
VoipPushNotification.addEventListener('didLoadWithEvents', async events => {
// --- this will fire when there are events occured before js bridge initialized
// --- use this event to execute your event handler manually by event type
if (!events || !Array.isArray(events) || events.length < 1) {
return
}
for (let voipPushEvent of events) {
let {name, data} = voipPushEvent
if (name === VoipPushNotification.RNVoipPushRemoteNotificationsRegisteredEvent) {
console.log('adding voip token in didLoadWithEvents', data)
await this.addVoipTokenToServer(data)
} else if (name === VoipPushNotification.RNVoipPushRemoteNotificationReceivedEvent) {
console.log('onVoipPushNotificationiReceived :>> ', data)
// RNCallKeep.displayIncomingCall(data?.reference, 'patientID', '34747')
}
}
})
VoipPushNotification.addEventListener('register', async token => {
// --- send token to your apn provider server
console.log('VoipPushNotification register event token:>> ', token)
await this.addVoipTokenToServer(token)
})
VoipPushNotification.addEventListener('notification', notification => {
// --- when receive remote voip push, register your VoIP client, show local notification ... etc
// --- optionally, if you `addCompletionHandler` from the native side, once you have done the js jobs to initiate a call, call `completion()`
// RNCallKeep.displayIncomingCall('44713e33-fa78-4ff5-8ec5-983e0832d1c6', 'Test', 'handle')
})
}
removeEventListeners = () => {}
addVoipTokenToServer = async token => {
const patientID = await getUserID()
let res = await addVoipDeviceToken(patientID, token)
console.log('res addVoipTokenToServer:>> ', res)
}
}
then in my App.js I import VoipNotificationInterface
and instantiate it
App.js
VoipNotificationInterface.getInstance()
Please I can get this to work.
My expected behaviour
I cant seem to when this to work. please an help will do
Breaking Change
This PR does the following:
remove unused codes, keep this lib simple, lots functionalities should be easily implemented in the modern RN eco system
PushNotificationIOS
instead )AppState
instead )use
NativeEventEmitter
instead the deprecatedDeviceEventEmitter
tweak event handler logic
support caching events before js initialize you can subscribe
didLoadWithEvents
when app readyAbout didLoadWithEvents
This is aim to fix #59 ( voip push received and the event fired before js initialized )
@JJMoon proposed a workaround like in PR #65 and a proper fix in PR #61 Thanks!
But then I tend to use a different way which is the same with react-native-webrtc/react-native-callkeep/pull/205
The reason:
Instead using a promise method to get events directly, using event based with
startObserving
, we can make sure the bridge is up and READY for events, and this may prevent edge case between get initial events and subsequently fired events.We cache events if js is not up, this can be work together with, if you would like to use
voipRegistration
directly indidFnishLaunchingWithOptions
inAppDelegate.m
. This is described in https://github.com/react-native-webrtc/react-native-voip-push-notification/issues/59#issuecomment-691685841 by @chevonc . Not sure but I believe that helps in some situation. (We don't know how exactly PushKit works and when will it DELIVER voip push to us happily.)Side question and discussion
The thing I can not figure out is, when app killed, and the app can be wake up by the remote push and enter the
didFnishLaunchingWithOptions
, doesn't that mean we already have a valid voip token to RECEIVE voip push?Does PushKit invoke
didReceiveIncomingPushWithPayload
only when we have registered voip token? Do we recommend to movevoipRegistration
indidFnishLaunchingWithOptions
inAppDelegate.m
?