Introduction
I made a VOIP dialer app with react-native
. I'm using react-native-callkeep to show the user the default IOS call screen when receiving a call (On background and foreground), and i'm using react-native-push-notification to pop-up a local notification when a call is received too.
The problem
When i minimize the app (With the home button) and the app receives a call, he doesn't show neither the local notification or the call screen.but, if the call comes right after the minimize action, the screen and the notification shows up.
Tecnical explanation of what happens
Reading the logs, i see that IOS kills my app background processing when he prints the following line:
15:08:23.085311-0300 symptomsd com.vmaxtelecom.vmaxfone: Foreground: false
After this, nothing works on foreground neither background
Code explanation
Probably, the problem isn't with react-native-callkeep or with react-native-push-notification itself (Because they're work as intended). I think it's something with IOS Itself.
I found this thread wich defines exactly what happens to me.
My enabled capabilities
What i'm using
react-native version 0.61.5
Xcode version 11.5 (11E608c) on a MacOS 10.15.5 Catalina
IOS 13.4.1 on a Iphone 8
I'm using a release version of my project (The debug version does the same thing too)
My AppDelegate.m (Only the part after didFinishLaunchingWithOptions
matters the most)
#import "AppDelegate.h"#import <React/RCTBridge.h>#import <React/RCTBundleURLProvider.h>#import <React/RCTRootView.h>#import <UserNotifications/UserNotifications.h>#import <RNCPushNotificationIOS.h>#import "RNCallKeep.h"#if DEBUG#import <FlipperKit/FlipperClient.h>#import <FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h>#import <FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h>#import <FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.h>#import <SKIOSNetworkPlugin/SKIOSNetworkAdapter.h>#import <FlipperKitReactPlugin/FlipperKitReactPlugin.h>static void InitializeFlipper(UIApplication *application) { FlipperClient *client = [FlipperClient sharedClient]; SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults]; [client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]]; [client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]]; [client addPlugin:[FlipperKitReactPlugin new]]; [client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]]; [client start];}#endif@implementation AppDelegate- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{if ([UIApplication instancesRespondToSelector:@selector(registerUserNotificationSettings:)]){ [application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound categories:nil]];}#if DEBUG InitializeFlipper(application);#endif RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"VMAXFone" 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]; UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; center.delegate = self; return YES;}-(void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler{ completionHandler(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge);}- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge{#if DEBUG return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];#else return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];#endif}- (BOOL)application:(UIApplication *)applicationcontinueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void(^)(NSArray * __nullable restorableObjects))restorationHandler{ return [RNCallKeep application:application continueUserActivity:userActivity restorationHandler:restorationHandler];}- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings{ [RNCPushNotificationIOS didRegisterUserNotificationSettings:notificationSettings];}- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{ [RNCPushNotificationIOS didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];}- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfofetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{ [RNCPushNotificationIOS didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];}- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error{ [RNCPushNotificationIOS didFailToRegisterForRemoteNotificationsWithError:error];}- (void)userNotificationCenter:(UNUserNotificationCenter *)centerdidReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler{ [RNCPushNotificationIOS didReceiveNotificationResponse:response]; completionHandler();}- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification{ [RNCPushNotificationIOS didReceiveLocalNotification:notification];}@end
i dont think this is necessary but every information counts, so here's how i set-up react-native-callkeep (This function is called whe the app starts)
function initCallKitIntegration() { return async function (dispatch, getState) { let activeCall = null const uuids = {} const options = { ios: { appName: 'VMAX Fone', imageName: 'phone_account_icon', includesCallsInRecents: true, supportsVideo: false } } RNCallKeep.setup(options).then(accepted => {}) let incomingCall = null const {endpoint} = getState().pjsip const {appState} = getState().pjsip endpoint.on("call_received", (call) => { RNCallKeep.addEventListener('answerCall', () => { //accept call here }) RNCallKeep.addEventListener('endCall', () => { //decline call here }) RNCallKeep.addEventListener('didPerformDTMFAction', ({ digits, callUUID }) => { //send dtmf digits here }) RNCallKeep.addEventListener('didPerformSetMutedCallAction', ({ muted, callUUID }) => { //mute and unmute call here }) RNCallKeep.addEventListener('didToggleHoldCallAction', ({ hold, callUUID }) => { //hold and unhold call here }) endpoint.on("call_changed", (call) => { //Change Call state here }) endpoint.on("call_terminated", (call) => { //endCall here RNCallKeep.endCall(uuids[call.getCallId()]) delete uuids[call.getCallId()] }) if (appState != 'active'){ RNCallKeep.displayIncomingCall(uuids[call.getCallId()], call.getRemoteNumber(), call.getRemoteNumber(), 'number', false) } }) }}
The logs
15:08:22 VMAXFone Snapshotting a view (0x107269800, UIKeyboardImpl) that has not been rendered at least once requires afterScreenUpdates:YES.15:08:22 powerd Process runningboardd.28 Created SystemIsActive "application<com.vmaxtelecom.vmaxfone>28-56-1455:FBSceneSnapshotAction:sceneID:com.vmaxtelecom.vmaxfone-default" age:00:00:00 id:51539643472 [System: PrevIdle SysAct]15:08:22 SpringBoard [sceneID:com.vmaxtelecom.vmaxfone-default] Sending scene action [SceneLifecycleEventOnly][0xa901] through WorkspaceServer: 0x2819e430015:08:22 backboardd Lcurrent=258.9457 Lr=2.8323 DR=91.4263 factor=0.084515:08:23 SpringBoard Application process state changed for com.vmaxtelecom.vmaxfone: <SBApplicationProcessState: 0x280321380; pid: 476; taskState: Running; visibility: Background>15:08:23 SpringBoard [com.vmaxtelecom.vmaxfone] Generating image data for snapshot: <XBApplicationSnapshot: 0x10033e300; identifier: 5EA00F96-F22F-4CDA-907D-4E24FCC9119C; contentType: SceneContent>15:08:23 backboardd Lcurrent=258.9457 Lr=2.8347 DR=91.3501 factor=0.084615:08:23 backboardd Lcurrent=258.9457 Lr=2.8370 DR=91.2738 factor=0.084715:08:23 SpringBoard [com.vmaxtelecom.vmaxfone] Generating image data for snapshot: <XBApplicationSnapshot: 0x107662e60; identifier: DAA57ADA-4C38-4904-9139-C0782D20A8CA; variantID: downscaled; 15:08:23 SpringBoard [com.vmaxtelecom.vmaxfone] Snapshot data for <XBApplicationSnapshot: 0x10033e300; …4E24FCC9119C> written to file: /private/var/mobile/Containers/Data/Application/E4F918EF-7431-4A05-9845-344EB8B058DD/Library/SplashBoard/Snapshots/sceneID:com.vmaxtelecom.vmaxfone-default/5EA00F96-F22F-4CDA-907D-4E24FCC9119C@2x.ktx15:08:23 backboardd Lcurrent=258.9457 Lr=2.8394 DR=91.1976 factor=0.084815:08:23 powerlogHelperd {"msg":"CLCopyAppsUsingLocation", "event":"activity"}15:08:23 SpringBoard [com.vmaxtelecom.vmaxfone] Snapshot data for <XBApplicationSnapshot: 0x107662e60; …C0782D20A8CA> written to file: /private/var/mobile/Containers/Data/Application/E4F918EF-7431-4A05-9845-344EB8B058DD/Library/SplashBoard/Snapshots/sceneID:com.vmaxtelecom.vmaxfone-default/downscaled/DAA57ADA-4C38-4904-9139-C0782D20A8CA@2x.ktx15:08:23 backboardd Lcurrent=258.9457 Lr=2.8418 DR=91.1215 factor=0.084915:08:23 backboardd Lcurrent=258.9457 Lr=2.8441 DR=91.0455 factor=0.085015:08:23 kernel memorystatus: set assertion priority(3) target VMAXFone:47615:08:23 runningboardd [application<com.vmaxtelecom.vmaxfone>:476] Set jetsam priority to 3 [0] flag[1]15:08:23 runningboardd [daemon<com.apple.SpringBoard>:56] Ignoring jetsam update because this process is not memory-managed15:08:23 runningboardd Calculated state for application<com.vmaxtelecom.vmaxfone>: running-active (role: NonUserInteractive)15:08:23 runningboardd [application<com.vmaxtelecom.vmaxfone>:476] Set darwin role to: NonUserInteractive15:08:23 runningboardd [daemon<com.apple.SpringBoard>:56] Ignoring GPU update because this process is not GPU managed15:08:23 runningboardd Calculated state for daemon<com.apple.SpringBoard>: running-active (role: UserInteractiveNonFocal)15:08:23 backboardd Lcurrent=258.9457 Lr=2.8465 DR=90.9694 factor=0.085115:08:23 mediaserverd -CMSessionMgr- CMSessionMgrHandleApplicationStateChange: CMSession: Client com.vmaxtelecom.vmaxfone with pid '476' is now Background Running. Background entitlement: YES ActiveLongFormVideoSession: NO WhitelistedLongFormVideoApp NO15:08:23 mediaserverd AudioSessionServerImp.cpp:3517:HandleAudioSessionApplicationStateChangeListener: { "action":"application_state_changed", "session":{"ID":"0x1f672","PID":476,"name":"VMAXFone"}, "details":{"new_state":"Background Running"} }15:08:23 mediaserverd -CMSessionMgr- CMSessionMgrHandleApplicationStateChange: CMSession: Client (null) with pid '477' is now Background Running. Background entitlement: NO ActiveLongFormVideoSession: NO WhitelistedLongFormVideoApp NO15:08:23 mediaserverd -CMSessionMgr- CMSessionMgrHandleApplicationStateChange: CMSession: Sending stop command to (null) with pid '477' because client is not allowed to play in the background AND does not continue AirPlaying video when device locks15:08:23 mediaserverd -CMSessionMgr- CMSessionMgrHandleApplicationStateChange: CMSession: Client (null) with pid '405' is now Background Running. Background entitlement: YES ActiveLongFormVideoSession: NO WhitelistedLongFormVideoApp NO15:08:23 mediaserverd SSServerImp.cpp:1179:SystemSoundServerKillSoundsForPID: pid 477(ContactViewViewS)15:08:23 SpringBoard <private> setEnabledTopics <private> ignoredTopics <private> opportunisticTopics <private> sendToDaemon: YES15:08:23 apsd Looking up connection on peer: 1b848900 found <private>15:08:23 apsd <private>: connection set enabled topics from <private> to <private>15:08:23 apsd <private>: connection set opportunistic topics from <private> to <private>15:08:23 apsd <private> setEnabledTopics:<private> ignoredTopics:<private> opportunisticTopics:<private> forCategory UsesALS pretend NO change Downgraded15:08:23 apsd <private> cancelling any pending filter updates15:08:23 apsd <private> received topic update for Server pretend YES but there is no change.15:08:23 apsd <private> no change detected between the new and old server filter - cancelling any pending updates15:08:23 apsd <private> flush any pending work that ALS told us to queue for its managed topics <private>, processMode systemToken15:08:23 apsd <private> flush any pending work that ALS told us to queue for its managed topics <private>, processMode userToken15:08:23 apsd <private> received topic update for Normal pretend NO but there is no change.15:08:23 SpringBoard <private> setEnabledTopics <private> ignoredTopics <private> opportunisticTopics <private> sendToDaemon: NO15:08:23 locationd {"msg":"#CLIUA Marking change", "clientKey":"com.vmaxtelecom.vmaxfone", "reason":"Decaying in-use status from process state", "assertionLevel":3, "coming":1}15:08:23 locationd {"msg":"#CLIUA Marking change", "clientKey":"com.vmaxtelecom.vmaxfone", "reason":"Process state from RunningBoard", "assertionLevel":4, "coming":0}15:08:23 backboardd Lcurrent=258.9457 Lr=2.8489 DR=90.8935 factor=0.085215:08:23 CommCenter #I BundleID: <private> is no longer a foreground app15:08:23 backboardd Lcurrent=258.9457 Lr=2.8513 DR=90.8176 factor=0.085315:08:23 symptomsd NBSM Current state: idle, changed: systemForeground to 0 for net type 015:08:23 symptomsd NBSM Eligible to go to active15:08:23 symptomsd NBSM Current state: idle, changed: systemForeground to 0 for net type 0, eligible for active but constraints unsatisfied (0,0,0)15:08:23 symptomsd NBSM Current state: idle, changed: systemForeground to 0 for net type 0, ineligible for positive as nil pred, wifi (0x0) cell (0x0)15:08:23 symptomsd NBSM Current state: idle, changed: systemForeground to 0 for net type 0, ineligible for broken as nil pred, wifi (0x0) cell (0x0)15:08:23 symptomsd com.vmaxtelecom.vmaxfone: Foreground: false
If there's a concept, or something that will make my app dont being killed by the IOS itself, i will be grateful of your help.Thanks in advance.