diff --git a/docs/messaging/usage/index.md b/docs/messaging/usage/index.md index b5a5b1005c..c0e98bea9d 100644 --- a/docs/messaging/usage/index.md +++ b/docs/messaging/usage/index.md @@ -246,7 +246,7 @@ Although the library supports handling messages in background/quit states, the u On Android, a [Headless JS](https://reactnative.dev/docs/headless-js-android) task (an Android only feature) is created that runs separately to your main React component; allowing your background handler code to run without mounting your root component. On iOS however, when a message is received the device silently starts your application in a background state. At this point, your background handler (via `setBackgroundMessageHandler`) is triggered, but your root React component also gets mounted. This can be problematic for some users since any side-effects will be called inside of your app (e.g. `useEffects`, analytics events/triggers etc). To get around this problem, -the messaging module injects a `isHeadless` prop to your root component which you can conditionally use to render/do "nothing" if your app is launched in the background: +you can configure your `AppDelegate.m` file (see instructions below) to inject a `isHeadless` prop into your root component. Use this property to conditionally render `null` ("nothing") if your app is launched in the background: ```jsx // index.js @@ -273,7 +273,26 @@ function App{) { AppRegistry.registerComponent('app', () => HeadlessCheck); ``` -On Android, this prop will not exist. +To inject a `isHeadless` prop into your app, please update your `AppDelegate.m` file as instructed below: + +```obj-c +// add this import statement at the top of your `AppDelegate.m` file +#import "RNFBMessagingModule.h" + +// in "(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions" method +// Use `addCustomPropsToUserProps` to pass in props for initialization of your app +// Or pass in `nil` if you have none as per below example +// For `withLaunchOptions` please pass in `launchOptions` object +NSDictionary *appProperties = [RNFBMessagingModule addCustomPropsToUserProps:nil withLaunchOptions:launchOptions]; + +// Find the `RCTRootView` instance and update the `initialProperties` with your `appProperties` instance +RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge + moduleName:@"nameOfYourApp" + initialProperties:appProperties]; +``` + + +On Android, the `isHeadless` prop will not exist. ### Topics diff --git a/packages/messaging/ios/RNFBMessaging/RNFBMessaging+NSNotificationCenter.m b/packages/messaging/ios/RNFBMessaging/RNFBMessaging+NSNotificationCenter.m index 13e37b7a15..73bb0c0bcd 100644 --- a/packages/messaging/ios/RNFBMessaging/RNFBMessaging+NSNotificationCenter.m +++ b/packages/messaging/ios/RNFBMessaging/RNFBMessaging+NSNotificationCenter.m @@ -136,9 +136,12 @@ - (void)application_onDidFinishLaunchingNotification:(nonnull NSNotification *)n if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) { if (rctRootView != nil) { NSMutableDictionary *appPropertiesDict = rctRootView.appProperties != nil ? [rctRootView.appProperties mutableCopy] : [NSMutableDictionary dictionary]; - appPropertiesDict[@"isHeadless"] = @([RCTConvert BOOL:@(YES)]); - rctRootView.appProperties = appPropertiesDict; + if([appPropertiesDict objectForKey:@"isHeadless"] != nil && [appPropertiesDict[@"isHeadless"] isEqual:@([RCTConvert BOOL:@(NO)])]) { + appPropertiesDict[@"isHeadless"] = @([RCTConvert BOOL:@(YES)]); + rctRootView.appProperties = appPropertiesDict; + } } + #if !(TARGET_IPHONE_SIMULATOR) // When an app launches in the background (BG mode) and is launched with the notification launch option the app delegate method // application:didReceiveRemoteNotification:fetchCompletionHandler: will not get called unless registerForRemoteNotifications @@ -151,15 +154,21 @@ - (void)application_onDidFinishLaunchingNotification:(nonnull NSNotification *)n } else { if (rctRootView != nil) { NSMutableDictionary *appPropertiesDict = rctRootView.appProperties != nil ? [rctRootView.appProperties mutableCopy] : [NSMutableDictionary dictionary]; - appPropertiesDict[@"isHeadless"] = @([RCTConvert BOOL:@(NO)]); - rctRootView.appProperties = appPropertiesDict; + if([appPropertiesDict objectForKey:@"isHeadless"] != nil && [appPropertiesDict[@"isHeadless"] isEqual:@([RCTConvert BOOL:@(YES)])]) { + appPropertiesDict[@"isHeadless"] = @([RCTConvert BOOL:@(NO)]); + rctRootView.appProperties = appPropertiesDict; + } + } } } else { if (rctRootView != nil) { NSMutableDictionary *appPropertiesDict = rctRootView.appProperties != nil ? [rctRootView.appProperties mutableCopy] : [NSMutableDictionary dictionary]; - appPropertiesDict[@"isHeadless"] = @([RCTConvert BOOL:@(NO)]); - rctRootView.appProperties = appPropertiesDict; + if([appPropertiesDict objectForKey:@"isHeadless"] != nil && [appPropertiesDict[@"isHeadless"] isEqual:@([RCTConvert BOOL:@(YES)])]) { + appPropertiesDict[@"isHeadless"] = @([RCTConvert BOOL:@(NO)]); + rctRootView.appProperties = appPropertiesDict; + } + } } } @@ -175,8 +184,10 @@ - (void)application_onDidEnterForeground { RCTRootView *rctRootView = (RCTRootView *) [UIApplication sharedApplication].delegate.window.rootViewController.view; if (rctRootView.appProperties != nil && rctRootView.appProperties[@"isHeadless"] == @(YES)) { NSMutableDictionary *appPropertiesDict = [rctRootView.appProperties mutableCopy]; - appPropertiesDict[@"isHeadless"] = @([RCTConvert BOOL:@(NO)]); - rctRootView.appProperties = appPropertiesDict; + if([appPropertiesDict objectForKey:@"isHeadless"] != nil && [appPropertiesDict[@"isHeadless"] isEqual:@([RCTConvert BOOL:@(YES)])]) { + appPropertiesDict[@"isHeadless"] = @([RCTConvert BOOL:@(NO)]); + rctRootView.appProperties = appPropertiesDict; + } } } } diff --git a/packages/messaging/ios/RNFBMessaging/RNFBMessagingModule.h b/packages/messaging/ios/RNFBMessaging/RNFBMessagingModule.h index 21fd3f02f4..00d2db9ef2 100644 --- a/packages/messaging/ios/RNFBMessaging/RNFBMessagingModule.h +++ b/packages/messaging/ios/RNFBMessaging/RNFBMessagingModule.h @@ -20,5 +20,5 @@ @interface RNFBMessagingModule : NSObject - ++ (NSDictionary *)addCustomPropsToUserProps:(NSDictionary *_Nullable)userProps withLaunchOptions:(NSDictionary *_Nullable)launchOptions; @end diff --git a/packages/messaging/ios/RNFBMessaging/RNFBMessagingModule.m b/packages/messaging/ios/RNFBMessaging/RNFBMessagingModule.m index 18c2b80951..2a44c7a1f9 100644 --- a/packages/messaging/ios/RNFBMessaging/RNFBMessagingModule.m +++ b/packages/messaging/ios/RNFBMessaging/RNFBMessagingModule.m @@ -44,6 +44,19 @@ + (BOOL)requiresMainQueueSetup { return YES; } ++ (NSDictionary *)addCustomPropsToUserProps:(NSDictionary *_Nullable)userProps withLaunchOptions:(NSDictionary *_Nullable)launchOptions { + NSMutableDictionary *appProperties = userProps != nil ? [userProps mutableCopy] : [NSMutableDictionary dictionary]; + appProperties[@"isHeadless"] = @([RCTConvert BOOL:@(NO)]); + + if (launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]) { + if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) { + appProperties[@"isHeadless"] = @([RCTConvert BOOL:@(YES)]); + } + } + + return [NSDictionary dictionaryWithDictionary:appProperties]; +} + - (NSDictionary *)constantsToExport { NSMutableDictionary *constants = [NSMutableDictionary new]; constants[@"isAutoInitEnabled"] = @([RCTConvert BOOL:@([FIRMessaging messaging].autoInitEnabled)]); diff --git a/tests/ios/testing/AppDelegate.m b/tests/ios/testing/AppDelegate.m index 65e3e5c923..3d8e528792 100644 --- a/tests/ios/testing/AppDelegate.m +++ b/tests/ios/testing/AppDelegate.m @@ -16,7 +16,7 @@ */ #import "AppDelegate.h" - +#import "RNFBMessagingModule.h" #import #import #import @@ -32,9 +32,11 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( NSURL *jsCodeLocation; jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; + NSDictionary *appProperties = [RNFBMessagingModule addCustomPropsToUserProps:nil withLaunchOptions:launchOptions]; + RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation moduleName:@"testing" - initialProperties:nil + initialProperties:appProperties launchOptions:launchOptions];