Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

getToken resolves to null if called too early #1286

Closed
erandagan opened this issue Jul 8, 2018 · 5 comments
Closed

getToken resolves to null if called too early #1286

erandagan opened this issue Jul 8, 2018 · 5 comments
Assignees
Labels
platform: ios plugin: messaging FCM only - ( messaging() ) - do not use for Notifications Workflow: Waiting for User Response Blocked waiting for user response.

Comments

@erandagan
Copy link

erandagan commented Jul 8, 2018

Background

  • messaging.getToken returns a Promise<string>, which should resolve to a valid FCM token OR reject with an error (eg. timeout).
  • Getting the FCM token is an asynchronous operation, even on the native side.
  • The current implementation of getToken on iOS looks something like this:
  resolve([[FIRInstanceID instanceID] token]);

Issue

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.

Expected behaviour

  • The promise should wait for instanceID.token to be populated and then resolve.
  • The promise should never resolve with null values

Reproduction

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?

Dirty & Temporary Fixes Until Resolution

  • Delaying (eg. await sleep(T)) the call to getToken in the first launch makes the problem go away, and getToken resolves as it should.
  • Implementing a simple polling logic

Suggested Solution

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
  }
}];

Environment

  1. Application Target Platform:
    Verified on iOS, didn't check on Android

  2. Development Operating System:
    macOS

  3. Build Tools:
    Xcode 9.4

  4. React Native version:
    0.55.4

  5. React Native Firebase Version:
    4.2.x

  6. Firebase Module:
    messaging

  7. 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.

@erandagan
Copy link
Author

Ping @Ehesp @chrisbianca @Salakar , could you have a look?

@Salakar Salakar self-assigned this Jul 19, 2018
@Salakar Salakar added this to the v5.0.0 Release milestone Jul 19, 2018
@Salakar
Copy link
Member

Salakar commented Jul 19, 2018

@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:

@Salakar Salakar added platform: ios Workflow: Waiting for User Response Blocked waiting for user response. plugin: messaging FCM only - ( messaging() ) - do not use for Notifications labels Jul 19, 2018
@erandagan
Copy link
Author

@Salakar Yup, that seems to fix it entirely 👍

@Salakar
Copy link
Member

Salakar commented Jul 20, 2018

@erandagan thanks for confirming, have pushed up something for this to master. 👍

@namvoeh
Copy link

namvoeh commented Aug 7, 2019

@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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
platform: ios plugin: messaging FCM only - ( messaging() ) - do not use for Notifications Workflow: Waiting for User Response Blocked waiting for user response.
Projects
None yet
Development

No branches or pull requests

3 participants