messaging.getToken returns a Promise<string>, which should resolve to a valid FCM token OR reject with an error (eg. timeout).getToken on iOS looks something like this:  resolve([[FIRInstanceID instanceID] token]);
When getToken gets called very early during the first launch of the app, it may resolve with null-ish values, since instanceID.token wasn't retrieved by FCM yet. This may lead to unexpected behaviour, since the promise resolves (not rejects) with a null value.
I'm experiencing this issue on a production app, where our usage something like this (using redux-saga)
function* someSaga() {
  yield take(LAUNCH);
  ....
  ....
  const isAuthenticated = yield select(isUserAuthenticated);
  if (!isAuthenticated) {
    yield call([firebase.messaging(), 'getToken']);
  }
}
This bug is paticularly difficult to spot since it only happens in release builds, perhaps due to the fact that dev mode builds take longer to boot, delaying the call to getToken?
await sleep(T)) the call to getToken in the first launch makes the problem go away, and getToken resolves as it should.Use instanceIDWithHandler from the Firebase SDK instead of accessing the token directly.
Quoting the FCM docs:
[[FIRInstanceID instanceID] instanceIDWithHandler:^(FIRInstanceIDResult * _Nullable result,
                                                    NSError * _Nullable error) {
  if (error != nil) {
    // We can reject the RN promise here
  } else {
    // We can resolve the RN promise here
  }
}];
Application Target Platform:
Verified on iOS, didn't check on Android
Development Operating System:
macOS
Build Tools:
Xcode 9.4
React Native version:
0.55.4
React Native Firebase Version:
4.2.x
Firebase Module:
messaging
Are you using typescript?
Yes
I'd be happy to submit a pull request with the suggested solution if a contributor would review this issue and confirm this is really the reason for the bug.
Ping @Ehesp @chrisbianca @Salakar , could you have a look?
@erandagan thanks for taking the time to put together this issue report and proposed solutions.
Could you try my updated getToken replacement for iOS below and report back if you continue to have this issue or if everything is OK. Once confirmed OK I can get this added into a release.
RCT_EXPORT_METHOD(getToken:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
    if (initialToken) {
        resolve(initialToken);
    } else if ([[FIRInstanceID instanceID] token]) {
        resolve([[FIRInstanceID instanceID] token]);
    } else {
        NSString * senderId = [[FIRApp defaultApp] options].GCMSenderID;
        [[FIRMessaging messaging] retrieveFCMTokenForSenderID:senderId completion:^(NSString * _Nullable FCMToken, NSError * _Nullable error) {
            if (error) {
                reject(@"messaging/fcm-token-error", @"Failed to retrieve FCM token.", error);
            } else if (FCMToken) {
                resolve(FCMToken);
            } else {
                resolve([NSNull null]);
            }
        }];
    }
}
Loving react-native-firebase and the support we provide? Please consider supporting us with any of the below:
React Native Firebase and Invertase on Twitter @Salakar Yup, that seems to fix it entirely 馃憤
@erandagan thanks for confirming, have pushed up something for this to master. 馃憤
@Salakar @erandagan is it safe to retry get token if getting this error Failed to retrieve FCM token.? Is there a way to asking Firebase that instanceID.token is retrieved then it's safe to get token?
Most helpful comment
@Salakar @erandagan is it safe to retry get token if getting this error
Failed to retrieve FCM token.? Is there a way to asking Firebase thatinstanceID.tokenis retrieved then it's safe to get token?