I am using the react-native-callkeep library in a React Native project to handle displaying incoming calls on receiving notification from firebase. The library works perfectly when the app is active, but fails to display incoming calls or wake up the app when it’s in the background or terminated on iOS.
PS: firebase notification works fine for both android and ios
Here’s the relevant part of my code:
AppDelegate.m:
#import "AppDelegate.h"
#import "Adjust.h"
#import "Firebase.h"
#import <UserNotifications/UserNotifications.h>
#import <FirebaseMessaging/FirebaseMessaging.h>
#import "FirebaseCore.h"
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import "RNCallKeep.h"
#import <React/RCTLinkingManager.h>
#import <PushKit/PushKit.h>
#import "RNVoipPushNotificationManager.h"
#import <RNCPushNotificationIOS.h>
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[FIRApp configure];
[RNCallKeep setup:@{@"appName": @"DocMoonlight", @"maximumCallGroups": @3, @"maximumCallsPerCallGroup": @1, @"supportsVideo": @YES}];
[RNVoipPushNotificationManager voipRegistration];
// other initialization code
}
- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(PKPushType)type withCompletionHandler:(void (^)(void))completion {
[RNVoipPushNotificationManager didReceiveIncomingPushWithPayload:payload forType:(NSString *)type];
NSString *uuid = [[[NSUUID UUID] UUIDString] lowercaseString];
[RNCallKeep reportNewIncomingCall:uuid handle:@"unknown" handleType:@"generic" hasVideo:NO localizedCallerName:@"Unknown" supportsHolding:YES supportsDTMF:YES supportsGrouping:YES supportsUngrouping:YES fromPushKit:YES payload:nil withCompletionHandler:completion];
}
React Native Side (App.js):
const options = {
ios: {
appName: 'DocMoonlight',
},
android: {
alertTitle: 'Permissions required',
alertDescription: 'This application needs to access your phone accounts',
cancelButton: 'Cancel',
okButton: 'ok',
imageName: '',
additionalPermissions: [],
foregroundService: {
channelId: 'com.docmoonlight',
channelName: 'Foreground service for my app',
notificationTitle: 'My app is running on background',
notificationIcon: 'ic_notification',
},
},
};
CallKeep.setup(options).then(() => {});
CallKeep.setAvailable(true);
this is the code used to handle display calls or normal notifications
async function onMessageReceived(remoteMessage) {
if (remoteMessage.data?.type === 'video_call') {
const callUUID = uuidv4();
CallKeep.displayIncomingCall(
callUUID,
remoteMessage.data?.location,
'Docmoonlight',
'generic',
true,
);
// Listen to the CallKeep events
CallKeep.addEventListener('answerCall', async ({callUUID}) => {
bringAppToForeground();
CallKeep.endCall(callUUID);
setTimeout(async () => {
if (remoteMessage.data) {
const zoomLink = remoteMessage.data;
try {
const jwtToken = generateSignature(zoomLink.meetingNumber);
await ZoomUs.initialize({
jwtToken,
})
.then(result => {
console.log(result);
try {
ZoomUs.joinMeeting({
userName: zoomLink.name,
meetingNumber: zoomLink.meetingNumber,
password: zoomLink.password,
});
} catch (error) {
console.error('Failed to join the meeting', error);
}
})
.catch(error => console.log('Failed to initialize', error));
} catch (error) {
console.log(error);
}
}
}, 1000);
});
CallKeep.addEventListener('endCall', ({callUUID}) => {
console.log('Call ended:', callUUID);
CallKeep.endCall(callUUID);
});
} else {
onDisplayNotification(
remoteMessage.notification?.title,
remoteMessage.notification?.body,
remoteMessage.messageId,
);
}
}
and this is my pod file
require_relative '../node_modules/react-native/scripts/react_native_pods'
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
platform :ios, min_ios_version_supported
prepare_react_native_project!
source 'https://github.com/CocoaPods/Specs.git'
# Disable Flipper temporarily
flipper_config = FlipperConfiguration.disabled
linkage = ENV['USE_FRAMEWORKS']
if linkage != nil
Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green
use_frameworks! :linkage => linkage.to_sym
end
target 'DocMoonlight' do
config = use_native_modules!
pod 'Firebase', :modular_headers => true
pod 'FirebaseCoreInternal', :modular_headers => true
pod 'GoogleUtilities', :modular_headers => true
pod 'FirebaseCore', :modular_headers => true
pod 'RNPermissions', :path => '../node_modules/react-native-permissions'
permissions_path = '../node_modules/react-native-permissions/ios'
pod 'FirebaseInstallations', :modular_headers => true
pod 'FirebaseCoreExtension', :modular_headers => true
pod 'GoogleDataTransport', :modular_headers => true
pod 'nanopb', :modular_headers => true
pod 'RNCallKeep', :path => '../node_modules/react-native-callkeep'
# Flags change depending on the env values.
flags = get_default_flags()
use_react_native!(
:path => config[:reactNativePath],
:hermes_enabled => flags[:hermes_enabled],
:fabric_enabled => flags[:fabric_enabled],
:flipper_configuration => flipper_config,
:app_path => "#{Pod::Config.instance.installation_root}/.."
)
target 'DocMoonlightTests' do
inherit! :complete
end
post_install do |installer|
react_native_post_install(
installer,
:mac_catalyst_enabled => false
)
end
end
and this is info.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>com.docmoonlight.app.fetch-updates</string>
</array>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>Docmoonlight</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>localhost</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>We will use your Bluetooth to access your Bluetooth headphones.</string>
<key>NSCameraUsageDescription</key>
<string>For people to see you during meetings, we need access to your camera.</string>
<key>NSDocumentsFolderUsageDescription</key>
<string>Docmoonlight's app requires access to your Documents Folder to allow you to upload necessary documents and files, such as proof of medical licensing or certifications, during the registration process</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Docmoonlight's app requires access to your location while in use to provide accurate clock-in and clock-out times for payment purposes. </string>
<key>NSMicrophoneUsageDescription</key>
<string>For people to hear you during meetings, we need access to your microphone.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Docmoonlight's app requires access to your Photo Library and file storage to allow you to upload necessary documents and files, such as proof of medical licensing or certifications, during the registration process</string>
<key>UIAppFonts</key>
<array>
<string>Poppins-Medium.ttf</string>
<string>Poppins-Regular.ttf</string>
<string>Poppins-SemiBold.ttf</string>
<string>AntDesign.ttf</string>
<string>Entypo.ttf</string>
<string>EvilIcons.ttf</string>
<string>Feather.ttf</string>
<string>FontAwesome.ttf</string>
<string>FontAwesome5_Brands.ttf</string>
<string>FontAwesome5_Regular.ttf</string>
<string>FontAwesome5_Solid.ttf</string>
<string>Foundation.ttf</string>
<string>Ionicons.ttf</string>
<string>MaterialIcons.ttf</string>
<string>MaterialCommunityIcons.ttf</string>
<string>SimpleLineIcons.ttf</string>
<string>Octicons.ttf</string>
<string>Zocial.ttf</string>
<string>FontAwesome6_Regular.ttf</string>
<string>FontAwesome6_Solid.ttf</string>
<string>FontAwesome6_Brands.ttf</string>
</array>
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
<string>fetch</string>
<string>remote-notification</string>
<string>voip</string>
</array>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
<string>UIInterfaceOrientationPortrait</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>
</plist>
i have tried so many tries on this but it doesnt work