react-native-webrtc / react-native-webrtc/react-native-callkeep
iOS voip push notifications don't work when app is open
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 1.1k
- Forks
- 511
- Avg merge
- 9h 12m
- Merged PRs (30d)
- 2
Description
Bug report(Question?)
-
I've checked the example to reproduce the issue.
-
Reproduced on:
-
Android
-
iOS
Description
I've setup callkeep with APNs voip notifictaions and firebase notifications(for non-voip pushes) in an IOS app. When app is killed voip pushes are delived properly, the callkit screen is displayed and when user responds, they are navigated to the proper screen. However when app is active there's no sign of didReceiveIncomingPushWithPayload being called and when app is in the background, it crashes because apparently the completion method for voip pushes isn't being called.
Usually in the issues it's the other way around, as the killed app isn't being woken up properly, but here when the app is killed everything works fine. I'm not sure what's causing the issues but i guess it might have something to do with the fact that i'm using firebase notifications for other pushes, and that might be causing an issue for voip pushes.
I'm kinda stuck as to where to go from here and was wondering if someone can provide me with the reason behind this or a way to fix the issue
Thanks in advance😀
Steps to Reproduce
Versions
- Callkeep: 4.3.3
- React Native: 0.64.4
- iOS: 13
- Android:
- Phone model: Iphone 7, Iphone 6s
Logs
When app is in the background, the completion error for voip pushes is shown. when app is in the foreground, there doesn't seem to be any logs/responses, none of the event listerens are called in either situation.
Sample Code parts
// AppDelegate.m
// IOS push notificaion
// Required for the register event.
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
[RNCPushNotificationIOS didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
[RNNotifications 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
{
NSLog(@"Native notificatoin recieved");
// [RNCPushNotificationIOS didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
[RNNotifications didReceiveBackgroundNotification:userInfo withCompletionHandler:completionHandler];
}
// Required for the registrationError event.
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
// [RNCPushNotificationIOS didFailToRegisterForRemoteNotificationsWithError:error];
[RNNotifications didFailToRegisterForRemoteNotificationsWithError:error];
}
// Required for localNotification event
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
withCompletionHandler:(void (^)(void))completionHandler
{
[RNCPushNotificationIOS didReceiveNotificationResponse:response];
}
- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(PKPushType)type withCompletionHandler:(void (^)(void))completion {
NSLog(@"pushRegistry:didReceiveIncomingPushWithPayload:forType:withCompletionHandler:%@",
payload.dictionaryPayload);
NSString *uuid = [payload.dictionaryPayload valueForKey:@"call_uuid"];
NSString *callerName = [payload.dictionaryPayload valueForKey:@"user"];
NSString *handle = [payload.dictionaryPayload valueForKey:@"user"];
[RNVoipPushNotificationManager addCompletionHandler:uuid completionHandler:completion];
[RNVoipPushNotificationManager didReceiveIncomingPushWithPayload:payload forType:(NSString *)type];
[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];
completion();
}
// App.js
const CallKeepSetup = () => {
const logger = useLogger('CallKeep');
const navigation = useNavigation();
const dispatch = useDispatch();
const answerCall = ({ callUUID }) => {
logger.info("Call recieved, answering call...")
AsyncStorage.setItem('currentCallUUID', callUUID);
try {
logger.info("Starting Callkeep call")
RNCallKeep.startCall(callUUID, 'Police', "Police", 'generic', true);
logger.info("Call started successfully")
} catch(e) {
logger.error("Failed to start callkeep call")
}
setTimeout(() => {
logger.info("Setting current active call...")
try {
RNCallKeep.setCurrentCallActive(callUUID);
logger.info("Current active call set");
} catch(e) {
logger.error("Failed to set current call", {error: e});
}
}, 1000);
logger.info("Call started successfully, setting active call")
// On Android display the app when answering a video call
if (!isIOS) {
console.log('bringing app to foreground');
RNCallKeep.backToForeground();
}
logger.info("Setting current call UUID")
dispatch(setCurrentCallUUID(callUUID))
logger.info("Navigating to call screen")
navigation.navigate('Video Call');
};
const endCall = ({ callUUID }) => {
logger.info("Ending call")
try {
dispatch(endCallWithUUID(callUUID));
RNCallKeep.endAllCalls();
logger.info("Call ended")
} catch(e) {
logger.error("Failed to end call", { error: e})
}
};
const didDisplayIncomingCall = async ({ callUUID, payload, handle, }) => {
try {
logger.info('Recieved call with data', { payload, callUUID, handle });
dispatch(
startVideoChatWithouActivating({
token: payload.token,
room_name: payload.room_name,
call_id: payload.call_id,
friend: payload.friend,
callUUID,
call_uuid: callUUID,
// location: JSON.parse(data.location ?? '{}'),
})
);
logger.info('Call displayed with data', { payload, callUUID, handle });
} catch(e) {
logger.error('Failed to save call data', { payload, callUUID, handle });
}
}
const handlePreJSEvents = (events) => {
logger.info('PreJS events', { events });
for (let event of events) {
if (event.name == "RNCallKeepDidDisplayIncomingCall") {
logger.info('PreJS events: didDisplayIncomingCall', { event });
didDisplayIncomingCall(event.data)
} else if (event.name == 'RNCallKeepAnswerCall') {
logger.info('PreJS events: answerCall', { event });
answerCall(event.data)
} else if (event.name == 'RNCallKeepEndCall') {
logger.info('PreJS events: endCall', { event });
endCall(event.data)
}
}
}
const initializeCallKeep = () => {
try {
logger.info('Call keep initiated successfully');
RNCallKeep.setAvailable(true);
RNCallKeep.addEventListener('answerCall', answerCall);
RNCallKeep.addEventListener('didReceiveStartCallAction', answerCall);
RNCallKeep.addEventListener('endCall', endCall);
RNCallKeep.addEventListener('didDisplayIncomingCall', didDisplayIncomingCall);
if (isIOS) {
RNCallKeep.addEventListener('didLoadWithEvents', handlePreJSEvents);
}
} catch (err) {
logger.error('Failed to initialize call keep', {
error: err,
msg: err.message,
});
console.error('initializeCallKeep error:', err.message);
}
};
useEffect(() => {
initializeCallKeep();
loadInitialCall();
return () => {
RNCallKeep.removeEventListener('answerCall', answerCall);
RNCallKeep.removeEventListener('didReceiveStartCallAction', answerCall);
RNCallKeep.removeEventListener('endCall', endCall);
RNCallKeep.removeEventListener('didDisplayIncomingCall', didDisplayIncomingCall);
if (isIOS) {
RNCallKeep.removeEventListener('didLoadWithEvents', handlePreJSEvents);
}
};
}, []);
return <></>;
};
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the PushKit callbacks in AppDelegate.m and the CallKeep event listeners in App.js, then compare their setup with the linked example. Reproduce the foreground and background notification cases on iOS and trace whether the callbacks and completion handlers run. Done means the cause is identified and both notification states behave as expected.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- ios, javascript, objective-c, react-native
- Domain
- mobile
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 30/100