React Native 0.69.1中如何给AppDelegate.mm注入Firebase isHeadless属性
React Native 0.69.1 iOS 下注入 isHeadless 属性适配 Firebase 后台消息处理
问题描述
我正在开发基于React Native 0.69.1的项目,使用Firebase推送通知服务,已实现前台、后台及杀死状态下的推送接收。为了在后台接收推送时更新应用角标,我在index.js中添加了Firebase的setBackgroundMessageHandler方法,并通过HeadlessCheck组件处理isHeadless属性判断。但iOS原生环境中不存在isHeadless属性,需要将其注入到AppDelegate.mm文件中,而Firebase文档未适配React Native 0.69.1生成的AppDelegate.mm结构,求解决方案。
系统信息
System: OS: macOS 12.4 CPU: (8) arm64 Apple M1 Memory: 97.44 MB / 8.00 GB Shell: 5.8.1 - /bin/zsh Binaries: Node: 16.16.0 - /opt/homebrew/opt/node@16/bin/node Yarn: 1.22.19 - /opt/homebrew/bin/yarn npm: 8.11.0 - /opt/homebrew/opt/node@16/bin/npm Watchman: 2022.07.04.00 - /opt/homebrew/bin/watchman Managers: CocoaPods: 1.11.3 - /opt/homebrew/bin/pod SDKs: iOS SDK: Platforms: DriverKit 21.4, iOS 15.5, macOS 12.3, tvOS 15.4, watchOS 8.5 Android SDK: Not Found IDEs: Android Studio: 2020.3 AI-203.7717.56.2031.7583922 Xcode: 13.4.1/13F100 - /usr/bin/xcodebuild Languages: Java: 11.0.15 - /usr/bin/javac npmPackages: @react-native-community/cli: Not Found react: 18.0.0 => 18.0.0 react-native: 0.69.1 => 0.69.1 react-native-macos: Not Found npmGlobalPackages: *react-native*: Not Found
现有代码
index.js
import {AppRegistry} from 'react-native'; import App from './src/App'; import {name as appName} from './app.json'; import messaging from '@react-native-firebase/messaging'; // Register background handler messaging().setBackgroundMessageHandler(async remoteMessage => { //Update badge count in here }); function HeadlessCheck({isHeadless}) { if (isHeadless) { // App has been launched in the background by iOS, ignore return null; } return App; } AppRegistry.registerComponent(appName, () => HeadlessCheck);
AppDelegate.mm
#import "AppDelegate.h" #import "RNBootSplash.h" #import <React/RCTBridge.h> #import <React/RCTBundleURLProvider.h> #import <React/RCTRootView.h> #import <React/RCTAppSetupUtils.h> #import <UserNotifications/UserNotifications.h> #import <RNCPushNotificationIOS.h> #import <Firebase.h> #import <TSBackgroundFetch/TSBackgroundFetch.h> #if RCT_NEW_ARCH_ENABLED #import <React/CoreModulesPlugins.h> #import <React/RCTCxxBridgeDelegate.h> #import <React/RCTFabricSurfaceHostingProxyRootView.h> #import <React/RCTSurfacePresenter.h> #import <React/RCTSurfacePresenterBridgeAdapter.h> #import <ReactCommon/RCTTurboModuleManager.h> #import <react/config/ReactNativeConfig.h> static NSString *const kRNConcurrentRoot = @"concurrentRoot"; @interface AppDelegate () <RCTCxxBridgeDelegate, RCTTurboModuleManagerDelegate> { RCTTurboModuleManager *_turboModuleManager; RCTSurfacePresenterBridgeAdapter *_bridgeAdapter; std::shared_ptr<const facebook::react::ReactNativeConfig> _reactNativeConfig; facebook::react::ContextContainer::Shared _contextContainer; } @end #endif @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [FIRApp configure]; RCTAppSetupPrepareApp(application); RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; #if RCT_NEW_ARCH_ENABLED _contextContainer = std::make_shared<facebook::react::ContextContainer const>(); _reactNativeConfig = std::make_shared<facebook::react::EmptyReactNativeConfig const>(); _contextContainer->insert("ReactNativeConfig", _reactNativeConfig); _bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:bridge contextContainer:_contextContainer]; bridge.surfacePresenter = _bridgeAdapter.surfacePresenter; #endif NSDictionary *initProps = [self prepareInitialProps]; UIView *rootView = RCTAppSetupDefaultRootView(bridge, @"amigo", initProps); if (@available(iOS 13.0, *)) { rootView.backgroundColor = [UIColor systemBackgroundColor]; } else { rootView.backgroundColor = [UIColor whiteColor]; } 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; // [REQUIRED] Register BackgroundFetch [[TSBackgroundFetch sharedInstance] didFinishLaunching]; [RNBootSplash initWithStoryboard:@"BootSplash" rootView:rootView]; return YES; } //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); } // 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]; } /// This method controls whether the `concurrentRoot`feature of React18 is turned on or off. /// /// @see: https://reactjs.org/blog/2022/03/29/react-v18.html /// @note: This requires to be rendering on Fabric (i.e. on the New Architecture). /// @return: `true` if the `concurrentRoot` feture is enabled. Otherwise, it returns `false`. - (BOOL)concurrentRootEnabled { // Switch this bool to turn on and off the concurrent root return true; } - (NSDictionary *)prepareInitialProps { NSMutableDictionary *initProps = [NSMutableDictionary new]; #ifdef RCT_NEW_ARCH_ENABLED initProps[kRNConcurrentRoot] = @([self concurrentRootEnabled]); #endif return initProps; } - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge { #if DEBUG return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; #else return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; #endif } #if RCT_NEW_ARCH_ENABLED #pragma mark - RCTCxxBridgeDelegate - (std::unique_ptr<facebook::react::JSExecutorFactory>)jsExecutorFactoryForBridge:(RCTBridge *)bridge { _turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge delegate:self jsInvoker:bridge.jsCallInvoker]; return RCTAppSetupDefaultJsExecutorFactory(bridge, _turboModuleManager); } #pragma mark RCTTurboModuleManagerDelegate - (Class)getModuleClassFromName:(const char *)name { return RCTCoreModulesClassProvider(name); } - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker { return nullptr; } - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name initParams: (const facebook::react::ObjCTurboModule::InitParams &)params { return nullptr; } - (id<RCTTurboModule>)getModuleInstanceFromClass:(Class)moduleClass { return RCTAppSetupDefaultModuleFromClass(moduleClass); } #endif @end
解决方案
要在iOS的AppDelegate.mm中注入isHeadless属性,只需修改prepareInitialProps方法,添加应用后台启动的判断逻辑,将属性传递给RN端:
修改后的prepareInitialProps方法
- (NSDictionary *)prepareInitialProps { NSMutableDictionary *initProps = [NSMutableDictionary new]; #ifdef RCT_NEW_ARCH_ENABLED initProps[kRNConcurrentRoot] = @([self concurrentRootEnabled]); #endif // 注入isHeadless属性:判断应用是否后台启动 BOOL isHeadless = NO; if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) { isHeadless = YES; } initProps[@"isHeadless"] = @(isHeadless); return initProps; }
逻辑说明
当iOS应用因后台推送被唤醒时,applicationState会处于UIApplicationStateBackground状态,此时isHeadless会被设为YES并传递给HeadlessCheck组件,组件会返回null,避免启动完整的UI渲染流程,确保setBackgroundMessageHandler能正常执行角标更新逻辑。
后续你可以在setBackgroundMessageHandler中添加角标更新代码,比如使用Firebase Messaging的API直接修改:
messaging().setBackgroundMessageHandler(async remoteMessage => { // 获取当前角标数,加1后更新 const currentBadge = await messaging().getBadgeCount(); await messaging().setBadgeCount(currentBadge + 1); });
内容的提问来源于stack exchange,提问作者Chahin Aghrim
相关产品推荐
相关产品推荐

