invertase / react-native-firebase

🔥 A well-tested feature-rich modular Firebase implementation for React Native. Supports both iOS & Android platforms for all Firebase services.
https://rnfirebase.io
Other
11.69k stars 2.21k forks source link

🔥 [🐛] getAPNSToken() just hangs #7272

Closed kg-currenxie closed 5 months ago

kg-currenxie commented 1 year ago

Issue

Hi. For some reason, messaging().getAPNSToken() just hangs forever. Yes I'm using a real device.

(Auto init is off, swizzling is on)

// Called when user presses a button in a custom modal to allow permissions.
const status = await requestNotifications(['alert', 'sound', 'badge'])
if (status !== RESULTS.GRANTED) {
  return
}
messaging()
  .registerDeviceForRemoteMessages()
  .then(async () => {
    console.log(3)
    const needsToSetAPNSToken = await isEmulator()
    console.log(4)
    if (needsToSetAPNSToken && isIOS()) {
      await messaging().setAPNSToken('test')
    }
    console.log(5) <------------- all logs fine including this line
    const apnsToken = await messaging().getAPNSToken() // keeps waiting forever
    console.log(6) <------------- does not log
    const fcmToken = await messaging().getToken()
    console.log('Push Notification device token received: ', {
      fcmToken,
      apnsToken,
    })

I have tried adding

- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken {
  NSLog(@"DEVICE DID REGISTER:");
  NSLog(@"DEVICE TOKEN: %s", deviceToken);
}

- (void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError*)error {
    NSLog(@"DEVICE DID FAIL TO REGISTER: %s", error);
}

but I'm not sure if that's enough, or if they would actually be called? Though, they don't log anything at all (XCode debugger while having a device connected in release mode)

For the strange part; this has worked before, and no code has changed since (i know i know, sounds like BS) 😄 but i even went back a few commits, to where our QA team successfully tested a whole flow. Would there be any possibility of Apple having intermittent issues? Seems unlikely.

So the question is, what are the things that could make this function call just hang, without any error logs or warnings? For example, if i call getToken() without first calling getAPNSToken(), the library does a good job at printing warnings or errors with short explanations. But at this moment, there's just nothing :) Would love more insight from people with a deeper understanding of this 🙏


Project Files

Javascript

Click To Expand

#### `package.json`: ```json { "name": "my-mobile-react-native", "version": "0.0.1", "private": true, "main": "index.js", "scripts": { ... }, "dependencies": { "@notifee/react-native": "7.7.1", "@react-native-clipboard/clipboard": "1.7.0", "@react-native-community/async-storage": "1.12.1", "@react-native-community/masked-view": "0.1.11", "@react-native-community/netinfo": "9.3.7", "@react-native-community/slider": "3.0.3", "@react-native-firebase/app": "17.3.2", "@react-native-firebase/messaging": "17.3.2", "@react-navigation/bottom-tabs": "6.3.2", "@react-navigation/drawer": "6.4.2", "@react-navigation/native": "6.0.10", "@react-navigation/native-stack": "6.6.2", "@sentry/react-native": "3.2.11", "@tanstack/react-query": "4.29.12", "@twilio/twilio-verify-for-react-native": "https://github.com/twilio/twilio-verify-for-react-native.git", "@types/react-native-share": "3.3.2", "ajv": "8.2.0", "appcenter": "4.4.5", "appcenter-analytics": "4.4.5", "appcenter-crashes": "4.4.5", "assert": "2.0.0", "axios": "0.26.1", "base64-arraybuffer": "0.2.0", "core-js": "3.8.2", "date-fns": "2.13.0", "date-fns-tz": "1.3.4", "date-time-format-timezone": "1.0.22", "es6-promise": "4.2.8", "eslint-plugin-10x": "1.5.0", "final-form": "4.20.6", "final-form-calculate": "1.3.2", "format-string-by-pattern": "1.2.2", "husky": "4.3.8", "i18next": "19.8.5", "iban": "0.0.14", "immer": "9.0.6", "jsc-android": "250230.2.1", "lint-staged": "12.3.4", "lodash": "4.17.21", "lottie-ios": "3.4.0", "lottie-react-native": "5.1.4", "normalizr": "3.6.0", "querystring": "0.2.1", "ramda": "0.27.2", "react": "18.0.0", "react-dom": "16.8.6", "react-error-boundary": "3.1.3", "react-final-form": "6.3.0", "react-final-form-listeners": "1.0.3", "react-hooks-compose": "2.0.7", "react-i18next": "11.18.3", "react-native": "0.70.4", "react-native-asset": "2.0.0", "react-native-biometrics": "2.1.4", "react-native-blob-util": "0.16.2", "react-native-bootsplash": "3.1.2", "react-native-codegen": "0.0.13", "react-native-config": "1.4.2", "react-native-device-info": "10.3.0", "react-native-encrypted-storage": "4.0.2", "react-native-exit-app": "1.1.0", "react-native-gesture-handler": "2.5.0", "react-native-in-app-review": "4.2.1", "react-native-keyboard-manager": "4.0.13-16", "react-native-modal-wrapper": "3.1.1", "react-native-modalize": "2.0.13", "react-native-navigation-bar-color": "2.0.1", "react-native-pdf": "6.6.2", "react-native-permissions": "3.7.2", "react-native-reanimated": "2.11.0", "react-native-safe-area-context": "4.3.1", "react-native-screens": "3.13.1", "react-native-section-list-get-item-layout": "2.2.3", "react-native-share": "7.3.0", "react-native-svg": "13.9.0", "react-native-webview": "11.26.1", "react-redux": "7.2.3", "react-redux-promise-listener": "1.0.0", "react-router": "5.2.0", "reactxp": "2.0.0", "reactxp-imagesvg": "2.0.0", "redux": "4.0.5", "redux-promise-listener": "1.1.1", "redux-saga": "1.1.3", "redux-saga-jwt": "1.1.1-next.2", "reselect": "4.0.0", "round.js": "1.1.1", "typescript-fsa": "3.0.0", "typescript-fsa-reducers": "1.2.1", "zustand": "4.3.2" }, "devDependencies": { "@babel/core": "7.13.10", "@babel/plugin-proposal-decorators": "7.8.3", "@babel/preset-env": "7.16.8", "@babel/preset-react": "7.18.6", "@babel/preset-typescript": "7.17.12", "@babel/runtime": "7.17.9", "@types/enzyme": "3.10.8", "@types/hoist-non-react-statics": "3.3.1", "@types/iban": "0.0.32", "@types/jest": "29.2.2", "@types/lodash": "4.14.179", "@types/node": "15.14.3", "@types/ramda": "0.27.2", "@types/react": "18.0.0", "@types/react-final-form-listeners": "1.0.0", "@types/react-native": "0.69.0", "@types/react-redux": "7.1.24", "@typescript-eslint/eslint-plugin": "5.15.0", "@typescript-eslint/parser": "5.25.0", "babel-eslint": "10.1.0", "babel-loader": "8.1.0", "babel-plugin-module-resolver": "4.1.0", "babel-plugin-ramda": "2.0.0", "boxen": "4.2.0", "boxen-cli": "1.0.0", "cli-table3": "0.6.0", "detox": "20.0.1", "dot-object": "2.1.4", "enzyme": "3.11.0", "enzyme-adapter-react-16": "1.15.2", "enzyme-to-json": "3.4.4", "eslint": "7.32.0", "eslint-config-prettier": "8.3.0", "eslint-config-react-app": "7.0.1", "eslint-import-resolver-typescript": "2.7.0", "eslint-loader": "4.0.2", "eslint-plugin-detox": "1.0.0", "eslint-plugin-i18next": "5.1.1", "eslint-plugin-import": "2.26.0", "eslint-plugin-jsonc": "2.3.1", "eslint-plugin-prettier": "4.0.0", "eslint-plugin-ramda": "2.5.1", "eslint-plugin-react": "7.29.4", "eslint-plugin-react-hooks": "2.5.0", "eslint-plugin-simple-import-sort": "7.0.0", "eslint-plugin-typescript-sort-keys": "2.1.0", "hoist-non-react-statics": "3.3.2", "inquirer": "7.1.0", "jest": "29.3.1", "metro-react-native-babel-preset": "0.72.3", "opentype.js": "1.3.4", "patch-package": "6.2.0", "postinstall-postinstall": "2.1.0", "prettier": "2.7.1", "react-native-debugger-open": "0.3.24", "react-native-schemes-manager": "2.0.0", "react-native-svg-transformer": "1.0.0", "redux-devtools-extension": "2.13.8", "ts-jest": "29.0.3", "typescript": "4.6.3" }, "resolutions": { "@types/react": "18.0.0", "ansi-regex": "5.0.1", "async": "3.2.2", "hoist-non-react-statics": "3.3.2", "i18next": "20.3.5", "minimist": "1.2.5", "node-fetch": "2.6.7", "nth-check": "2.0.1", "react-native-svg": "13.9.0", "shell-quote": "1.7.3", "unset-value": "2.0.1" }, "xcodeSchemes": { "Debug": [ "Debug.Development", "Debug.Demo", "Debug.Preprod", "Debug.Staging", "Debug.Production" ], "Release": [ "Release.Development", "Release.Demo", "Release.Preprod", "Release.Staging", "Release.Production" ], "projectDirectory": "iOS" }, "husky": { "hooks": { "commit-msg": "node ./git-hooks/validate-commit-message.js ${HUSKY_GIT_PARAMS}", "pre-commit": "lint-staged", "pre-push": "yarn tsc", "prepare-commit-msg": "chmod +x ./git-hooks/commit-message.sh && ./git-hooks/commit-message.sh ${HUSKY_GIT_PARAMS}" } }, "reactNativePermissionsIOS": [ "AppTrackingTransparency", "Notifications" ], "lint-staged": { "**/*.rb": [ "bundle exec rubocop -a", "git add" ], "src/**/*.{ts,tsx}": [ "prettier --write", "eslint --fix", "git add" ], "src/configs/locales/*.json": [ "yarn translations:duplicates", "yarn translations:missing", "yarn translations:deleted" ] } } ``` #### `firebase.json` for react-native-firebase v6: ```json { "react-native": { "messaging_ios_auto_register_for_remote_messages": "false", "messaging_ios_foreground_presentation_options": [ "badge", "sound", "list", "banner" ] } } ```

iOS

Click To Expand

#### `ios/Podfile`: - [ ] I'm not using Pods - [x] I'm using Pods and my Podfile looks like: ```ruby source 'https://cdn.cocoapods.org/' project 'MyMobileReactNative', 'Debug.Development' => :debug, 'Debug.Demo' => :debug, 'Debug.Preprod' => :debug, 'Debug.Staging' => :debug, 'Debug.Production' => :debug, 'Release.Development' => :release, 'Release.Demo' => :release, 'Release.Preprod' => :release, 'Release.Staging' => :release, 'Release.Production' => :release require_relative '../node_modules/react-native/scripts/react_native_pods' require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' platform :ios, '12.4' install! 'cocoapods', deterministic_uuids: false # Some of the pods have an older deployment target, which fails the build # Force all pods to our target def fix_deployment_targets(installer) installer.pods_project.targets.each do |target| # RN-config fix: https://github.com/luggit/react-native-config/issues/365 if target.name == 'react-native-config' phase = target.project.new(Xcodeproj::Project::Object::PBXShellScriptBuildPhase) phase.shell_script = 'cd ../../'\ ' && RNC_ROOT=./node_modules/react-native-config'\ ' && export SYMROOT=$RNC_ROOT/ios/ReactNativeConfig'\ ' && ruby $RNC_ROOT/ios/ReactNativeConfig/BuildDotenvConfig.rb "${SRC_ROOT}/../"'\ ' "${SYMROOT}"' target.build_phases << phase target.build_phases.move(phase, 0) end target.build_configurations.each do |config| config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '11' end if target.name == 'RCT-Folly' target.build_configurations.each do |config| config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '9.0' end end end end target 'MyMobileReactNative' do config = use_native_modules! # https://rnfirebase.io/#altering-cocoapods-to-use-frameworks use_frameworks! linkage: :static # rubocop:disable Style/GlobalVars $RNFirebaseAsStaticFramework = true # rubocop:enable Style/GlobalVars # Flags change depending on the env values. # flags = get_default_flags use_react_native!( path: config[:reactNativePath], # to enable hermes on iOS, change `false` to `true` and then install pods # hermes_enabled: flags[:hermes_enabled], hermes_enabled: false, # fabric_enabled: flags[:fabric_enabled], fabric_enabled: false, # An absolute path to your application root. app_path: "#{Pod::Config.instance.installation_root}/..", flipper_configuration: FlipperConfiguration.disabled ) post_install do |installer| react_native_post_install( installer, # Set `mac_catalyst_enabled` to `true` in order to apply patches # necessary for Mac Catalyst builds mac_catalyst_enabled: false ) fix_deployment_targets(installer) end # Custom pods pod 'lottie-ios', path: '../node_modules/lottie-ios' pod 'lottie-react-native', path: '../node_modules/lottie-react-native' pod 'react-native-config', path: '../node_modules/react-native-config' pod 'react-native-in-app-review', path: '../node_modules/react-native-in-app-review' pod 'ReactNativeKeyboardManager', path: '../node_modules/react-native-keyboard-manager' pod 'RNDeviceInfo', path: '../node_modules/react-native-device-info' pod 'RNGestureHandler', path: '../node_modules/react-native-gesture-handler' pod 'RNSentry', path: '../node_modules/@sentry/react-native' pod 'RNSVG', path: '../node_modules/react-native-svg' end ``` #### `AppDelegate.mm`: ```objc #import "AppDelegate.h" #import #import #import #import #if RCT_NEW_ARCH_ENABLED #import #import #import #import #import #import #import static NSString *const kRNConcurrentRoot = @"concurrentRoot"; @interface AppDelegate () { RCTTurboModuleManager *_turboModuleManager; RCTSurfacePresenterBridgeAdapter *_bridgeAdapter; std::shared_ptr _reactNativeConfig; facebook::react::ContextContainer::Shared _contextContainer; } @end #endif // CUSTOM: Splash screen #import "RNBootSplash.h" // CUSTOM: Firebase #import // CUSTOM: Appcenter #import #import #import @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { RCTAppSetupPrepareApp(application); RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; #if RCT_NEW_ARCH_ENABLED _contextContainer = std::make_shared(); _reactNativeConfig = std::make_shared(); _contextContainer->insert("ReactNativeConfig", _reactNativeConfig); _bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:bridge contextContainer:_contextContainer]; bridge.surfacePresenter = _bridgeAdapter.surfacePresenter; #endif // CUSTOM: The name here has to be "RXApp" NSDictionary *initProps = [self prepareInitialProps]; UIView *rootView = RCTAppSetupDefaultRootView(bridge, @"RXApp", 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]; // CUSTOM: Splash screen [RNBootSplash initWithStoryboard:@"BootSplash" rootView:rootView]; // CUSTOM: Firebase [FIRApp configure]; // CUSTOM: Appcenter [AppCenterReactNative register]; [AppCenterReactNativeAnalytics registerWithInitiallyEnabled:true]; [AppCenterReactNativeCrashes registerWithAutomaticProcessing]; return YES; } - (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken { NSLog(@"DEVICE DID REGISTER:"); NSLog(@"DEVICE TOKEN: %s", deviceToken); } - (void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError*)error { NSLog(@"DEVICE DID FAIL TO REGISTER: %s", error); } /// 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)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)getTurboModule:(const std::string &)name jsInvoker:(std::shared_ptr)jsInvoker { return nullptr; } - (std::shared_ptr)getTurboModule:(const std::string &)name initParams: (const facebook::react::ObjCTurboModule::InitParams &)params { return nullptr; } - (id)getModuleInstanceFromClass:(Class)moduleClass { return RCTAppSetupDefaultModuleFromClass(moduleClass); } #endif @end ```


Environment

Click To Expand

**`react-native info` output:** ``` System: OS: macOS 13.5 CPU: (10) arm64 Apple M1 Max Memory: 109.11 MB / 32.00 GB Shell: 5.9 - /bin/zsh Binaries: Node: 14.19.1 - /var/folders/wl/rld35ngd74l38l3ljkg53hbm0000gn/T/yarn--1690970282310-0.8899052122383881/node Yarn: 1.22.17 - /var/folders/wl/rld35ngd74l38l3ljkg53hbm0000gn/T/yarn--1690970282310-0.8899052122383881/yarn npm: 6.14.16 - ~/.nvm/versions/node/v14.19.1/bin/npm Watchman: 2023.05.01.00 - /opt/homebrew/bin/watchman Managers: CocoaPods: 1.11.3 - /Users/frexuz/.rbenv/shims/pod SDKs: iOS SDK: Platforms: DriverKit 22.4, iOS 16.4, macOS 13.3, tvOS 16.4, watchOS 9.4 Android SDK: API Levels: 28, 30, 31, 32, 33 Build Tools: 30.0.2, 30.0.3, 31.0.0, 32.0.0, 33.0.0 System Images: android-29 | Google Play ARM 64 v8a, android-31 | Google APIs ARM 64 v8a Android NDK: Not Found IDEs: Android Studio: 2022.1 AI-221.6008.13.2211.9619390 Xcode: 14.3/14E222b - /usr/bin/xcodebuild Languages: Java: 17.0.7 - /usr/bin/javac npmPackages: @react-native-community/cli: Not Found react: 18.0.0 => 18.0.0 react-native: 0.70.4 => 0.70.4 react-native-macos: Not Found npmGlobalPackages: *react-native*: Not Found ``` - **Platform that you're experiencing the issue on**: - [x] iOS - [ ] Android - [ ] **iOS** but have not tested behavior on Android - [ ] **Android** but have not tested behavior on iOS - [ ] Both - **`react-native-firebase` version you're using that has this issue:** - "@react-native-firebase/app": "17.3.2", - "@react-native-firebase/messaging": "17.3.2", - **`Firebase` module(s) you're using that has the issue:** - messaging() - **Are you using `TypeScript`?** - "typescript": "4.6.3"


kg-currenxie commented 1 year ago

Update: I found this in the docs

- (void)messaging:(FIRMessaging *)messaging didReceiveRegistrationToken:(NSString *)fcmToken {
    NSLog(@"FCM registration token: %@", fcmToken);
    // Notify about received token.
    NSDictionary *dataDict = [NSDictionary dictionaryWithObject:fcmToken forKey:@"token"];
    [[NSNotificationCenter defaultCenter] postNotificationName:
     @"FCMToken" object:nil userInfo:dataDict];
    // TODO: If necessary send token to application server.
    // Note: This callback is fired at each app startup and whenever a new token is generated.
}

And it does log image

Though, its the FCM token, and not the APNs token. Is there an equivalent method for that?

From the docs

In the same way that Apple platforms typically deliver an APNs device token on app start, FCM provides a registration token via FIRMessagingDelegate's messaging:didReceiveRegistrationToken

kg-currenxie commented 1 year ago

Update 🎉 : There seems to be a difference with requesting permissions

Because of Android 13, we recently changed to using react-native-permissions instead of the one from messaging().requestPermission(). Using react-native-permissions have been suggested multiple times in this repo, so I never thought this could be related to APNs tokens.

Here's what made getting the token work again (by using the different permission requests per platform)

  requestPlatformPermission = async (): Promise<boolean> => {
    let status
    let isGranted = false
    if (isIOS()) {
      status = await messaging().requestPermission({
        alert: true,
        sound: true,
        badge: true,
      })
      isGranted = status === messaging.AuthorizationStatus.AUTHORIZED
    } else {
      status = await requestNotifications(['alert', 'sound', 'badge'])
      isGranted = status.status === RESULTS.GRANTED
    }
    return new Promise((resolve) => {
      resolve(isGranted)
    })
  }

and then using this instead before registering the device:

  const isGranted = await this.requestPlatformPermission()
  if (!isGranted) {
    return
  }

  messaging()
    .registerDeviceForRemoteMessages().then(
       ... getAPNSToken() etc ..

any idea why getAPNSToken() "only" works with the permission request from this library? Are the implementations coupled? It think no right? The permission is granted on an OS-level.

or are there any other details that I'm missing perhaps?

tux2nicolae commented 1 year ago

I'm having the same issue.

@kg-currenxie I'm trying to understand why this issue may be related to requestPermission(), I think the token should be obtain even without calling requestPermission() because you can use the messaging service as data only without notifications and you still need to have a valid token.

The same in our case, I'm pretty sure this worked before and returned null instead, I even created an issue here https://github.com/invertase/react-native-firebase/issues/6464 that the method returned null except the first time it registers for remote messages. The same seems to happen here, first time the app is installed it seems to return a valid token, the following calls (after the app is restarted) seem to hang (a few months before at least returned null instead)

kg-currenxie commented 1 year ago

I couldn't tell you why, just that it worked 😅 with the wrong permission call, getting the token always hung, and changing it made it work 🤷‍♂️

I think the token should be obtain even without calling requestPermission() because you can use the messaging service as data only without notifications and you still need to have a valid token.

well that makes sense too :)

samih-dev commented 1 year ago

this is critical bug I think specially for apps trying to get the APNS token (after this being a requirement) and the app is live already and has user logged in and registered their devices

for me this is happening on version 18.3.1

hollanderbart commented 1 year ago

How can an app even function with this bug? I'm at the point where I can better use the native iOS and Android libraries and go with that.

EDIT: I misread the documentation and missed the point where it tells to add the [FIRApp configure]; to the AppDelegate. https://rnfirebase.io/#configure-firebase-with-ios-credentials After that was added, everything worked again.

appa-gomi commented 1 year ago

add this line to node_modules/@react-native-firebase/messaging/ios/RNFBMessaging/RNFBMessagingModule.m!

RCT_EXPORT_METHOD(getAPNSToken : (RCTPromiseResolveBlock)resolve : (RCTPromiseRejectBlock)reject) {
  NSData *apnsToken = [FIRMessaging messaging].APNSToken;
  if (apnsToken) {
    resolve([RNFBMessagingSerializer APNSTokenFromNSData:apnsToken]);
  } else {
#if TARGET_IPHONE_SIMULATOR
#if !TARGET_CPU_ARM64
    DLog(@"RNFBMessaging getAPNSToken - Simulator without APNS support detected, with no token "
         @"set. Use setAPNSToken with an arbitrary string if needed for testing.")
        resolve([NSNull null]);
    return;
#endif
    DLog(@"RNFBMessaging getAPNSToken - ARM64 Simulator detected, but no APNS token set. Assuming "
         @"APNS token is possible. macOS13+ / iOS16+ / M1 mac required for assumption to be valid. "
         @"Use setAPNSToken in testing if needed.");
#endif
    if ([UIApplication sharedApplication].isRegisteredForRemoteNotifications == NO) {
      [RNFBSharedUtils
          rejectPromiseWithUserInfo:reject
                           userInfo:(NSMutableDictionary *)@{
                             @"code" : @"unregistered",
                             @"message" : @"You must be registered for remote messages before "
                                          @"calling getAPNSToken, see "
                                          @"messaging().registerDeviceForRemoteMessages().",
                           }];
      return;
    }
    // -----> this line
    resolve([NSNull null]);
    // <-------------
  }
}

this is work for me!

github-actions[bot] commented 1 year ago

Hello 👋, to help manage issues we automatically close stale issues.

This issue has been automatically marked as stale because it has not had activity for quite some time.Has this issue been fixed, or does it still require attention?

This issue will be closed in 15 days if no further activity occurs.

Thank you for your contributions.

samih-dev commented 1 year ago

still require attention

On Mon, 23 Oct 2023 at 04:51, github-actions[bot] @.***> wrote:

Hello 👋, to help manage issues we automatically close stale issues.

This issue has been automatically marked as stale because it has not had activity for quite some time.Has this issue been fixed, or does it still require attention?

This issue will be closed in 15 days if no further activity occurs.

Thank you for your contributions.

— Reply to this email directly, view it on GitHub https://github.com/invertase/react-native-firebase/issues/7272#issuecomment-1774309342, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAXWQKHXFWYOW6J7LGRKEP3YAXERPAVCNFSM6AAAAAA3BBI6DGVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTONZUGMYDSMZUGI . You are receiving this because you commented.Message ID: @.***>

--

בברכה סאמח אבועואד 0527362003

tux2nicolae commented 1 year ago

Still needs to be fixed, @appa-gomi 's solution works for us

github-actions[bot] commented 11 months ago

Hello 👋, to help manage issues we automatically close stale issues.

This issue has been automatically marked as stale because it has not had activity for quite some time.Has this issue been fixed, or does it still require attention?

This issue will be closed in 15 days if no further activity occurs.

Thank you for your contributions.

dusanristic commented 9 months ago

Hey, is there any progress on this? I started experiencing this issue after upgrading RN Firebase from v14 to v18.6.2. It doesn't happen always, but from time to time. I am using RN v0.72.7.

mikehardy commented 9 months ago

It doesn't happen always, but from time to time. I am using RN v0.72.7.

That means the code is functional or it would never work. A sometimes error is likely some sort of network interruption. Note that getting these remote messaging tokens relies on permissions (may not be granted?) and network requests (may time out or fail - APNs / FCM token fetch needs to be checked and retried perhaps with incremental backoff)

In the following comment I look more closely at the network failure case, I think that might be the key

mikehardy commented 9 months ago

@appa-gomi in your comment https://github.com/invertase/react-native-firebase/issues/7272#issuecomment-1732776694 it does look like you've found a resolution for the method call simply hanging.

There appears to be a combination of conditionals where no expected case is hit and the method falls through to the end but without resolving, which would explain the symptoms.

Your proposed resolution of the promise with null even seems correct for this case. The only addition might be to add some more DLogs, for example, to resolve with null as you propose, but immediately after the DLog indicating that it is a simulator that should be capable of getting an APNS token, but didn't.

Then have another DLog after the check for remote message registration where we note to developers that we are on a real device here and are registered, so we should have gotten a token but perhaps there was a network failure and they should retry. Then resolve with null

What do you think?

@hollanderbart and @vid3v - you reacted with a thumbs-down on the @appa-gomi proposed change. Is there a technical reason for that? Did you try it and it did not work? I would love to know if that was the case as it seems that change (or something functionally equivalent but just with more developer messaging) should work

dusanristic commented 8 months ago

@mikehardy shouldn't we also add resolve([NSNull null]) before return statement on line 190?

mikehardy commented 8 months ago

@dusanristic

I do not believe so. This shared utils function takes the reject block as a parameter and calls it, in order to meet it's named purpose of rejectPromiseWithUserInfo

https://github.com/invertase/react-native-firebase/blob/b30eee1b97b4290474c00607342befda55272075/packages/messaging/ios/RNFBMessaging/RNFBMessagingModule.m#L183

Alex-Whyatt commented 6 months ago

@mikehardy sorry to tag you but I'm wondering whether there's still weight to this issue - I think I'm experiencing it.

package versions "react-native": "0.72.6", "@react-native-firebase/analytics": "19.2.2", "@react-native-firebase/app": "19.2.2", "@react-native-firebase/messaging": "19.2.2", "expo": "49.0.13", "expo-build-properties": "0.11.1",
AppDelegate.mm (auto-generated) ```mm #import "AppDelegate.h" // @generated begin react-native-maps-import - expo prebuild (DO NOT MODIFY) sync-f2f83125c99c0d74b42a2612947510c4e08c423a #if __has_include() #import #endif // @generated end react-native-maps-import #import #import #import @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // @generated begin react-native-maps-init - expo prebuild (DO NOT MODIFY) sync-ba3e6dd042f7fe1b7ac3c7cee5c8029c482c489a #if __has_include() [GMSServices provideAPIKey:@"AIzaSyD8W8_fRaK2QvPeaXDYdak6lyWyHnkB6Qk"]; #endif // @generated end react-native-maps-init // @generated begin @react-native-firebase/app-didFinishLaunchingWithOptions - expo prebuild (DO NOT MODIFY) sync-ecd111c37e49fdd1ed6354203cd6b1e2a38cccda [FIRApp configure]; // @generated end @react-native-firebase/app-didFinishLaunchingWithOptions self.moduleName = @"main"; // You can add your custom initial props in the dictionary below. // They will be passed down to the ViewController used by React Native. self.initialProps = @{}; return [super application:application didFinishLaunchingWithOptions:launchOptions]; } - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge { #if DEBUG return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@".expo/.virtual-metro-entry"]; #else return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; #endif } // Linking API - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary *)options { return [super application:application openURL:url options:options] || [RCTLinkingManager application:application openURL:url options:options]; } // Universal Links - (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray> * _Nullable))restorationHandler { BOOL result = [RCTLinkingManager application:application continueUserActivity:userActivity restorationHandler:restorationHandler]; return [super application:application continueUserActivity:userActivity restorationHandler:restorationHandler] || result; } // Explicitly define remote notification delegates to ensure compatibility with some third-party libraries - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { return [super application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; } // Explicitly define remote notification delegates to ensure compatibility with some third-party libraries - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { return [super application:application didFailToRegisterForRemoteNotificationsWithError:error]; } // Explicitly define remote notification delegates to ensure compatibility with some third-party libraries - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { return [super application:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler]; } @end ```

We're experiencing this while using expo managed project setup for firebase, we have auto register set to false in our firebase.json, and have a setup like this:

const status = await messaging().hasPermission();

then register for remote notifications:

await messaging().registerDeviceForRemoteMessages();

At this point, isDeviceRegisteredForRemoteNotifications returns 'true' but calling await messaging().getToken(); returns this error: https://github.com/invertase/react-native-firebase/issues/6893. We can circumvent this by using these setter methods:

const tokenAPNS = await messaging().getAPNSToken();
await messaging().setAPNSToken(tokenAPNS);

However, getAPNSToken hangs when i've asked for permission with messaging.requestPermission(), but not react-native-permissions' requestNotifications, even when set with the same flags: {alert: true, sound: true, badge: true} - with the other library I can get & set device tokens.

mikehardy commented 6 months ago

I believe I am currently experiencing the same thing as a regression somehow, as I attempt to run e2e tests locally (and in CI) - but I haven't pinned it down yet. something is not working correctly, but it used to work 100% for me.

mikehardy commented 5 months ago

This was extremely frustrating! The details provided here - specifically everyone figuring out that permissions was the difference and that react-native-permissions in particular worked, meant I could find my way to a fix though.

The APIs should no longer hang indefinitely no matter what, and our requestPermission method will mirror what react-native-permissions does (an immediate notification registration on successful grant, basically)

This should work once #7797 is merged and released

Thanks @kg-currenxie @Alex-Whyatt et all, very helpful.

kg-currenxie commented 5 months ago

@mikehardy awesome!

Alex-Whyatt commented 5 months ago

@mikehardy thanks for looking into this!