Skip to content

Bug, firestore not working with react-native 0.71.3 #7115

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

Closed
superphil0 opened this issue Mar 11, 2023 · 15 comments
Closed

Bug, firestore not working with react-native 0.71.3 #7115

superphil0 opened this issue Mar 11, 2023 · 15 comments

Comments

@superphil0
Copy link

superphil0 commented Mar 11, 2023

[REQUIRED] Describe your environment

  • Operating System version: iOS 16.2
  • Browser version: react-native 0.71.3
  • Firebase SDK version: 9.17.2
  • Firebase Product: firestore

Describe the problem

May be related to #6993
I started out on this journey by wanting to use the firebase SDK for our react-native App.
All I want to do is to read from firestore with the firebase js SDK.
First time I ran it, I got a weird time out, here are the logs with setLogLevel("debug")

 LOG  Running "mobile" with {"rootTag":21,"initialProps":{}}
 LOG  [2023-03-11T12:47:15.666Z]  @firebase/firestore: Firestore (9.17.2): FirebaseAuthCredentialsProvider Auth not yet detected
 LOG  [2023-03-11T12:47:15.668Z]  @firebase/firestore: Firestore (9.17.2): FirestoreClient Using default OnlineComponentProvider
 LOG  [2023-03-11T12:47:15.669Z]  @firebase/firestore: Firestore (9.17.2): FirestoreClient Using default OfflineComponentProvider
 LOG  [2023-03-11T12:47:15.670Z]  @firebase/firestore: Firestore (9.17.2): FirestoreClient Initializing OfflineComponentProvider
 LOG  [2023-03-11T12:47:15.672Z]  @firebase/firestore: Firestore (9.17.2): FirestoreClient Initializing OnlineComponentProvider
 LOG  [2023-03-11T12:47:15.679Z]  @firebase/firestore: Firestore (9.17.2): MemoryPersistence Starting transaction: Allocate target
 LOG  [2023-03-11T12:47:15.687Z]  @firebase/firestore: Firestore (9.17.2): MemoryPersistence Starting transaction: Execute query
 LOG  [2023-03-11T12:47:15.691Z]  @firebase/firestore: Firestore (9.17.2): QueryEngine Using full collection scan to execute query: Query(target=Target(users, orderBy: [__name__ (asc)]); limitType=F)
 LOG  [2023-03-11T12:47:15.703Z]  @firebase/firestore: Firestore (9.17.2): Connection Creating WebChannel: https://firestore.googleapis.com/google.firestore.v1.Firestore/Listen/channel {"httpSessionIdParam":"gsessionid","initMessageHeaders":{"X-Goog-Api-Client":"gl-js/ fire/9.17.2","Content-Type":"text/plain","X-Firebase-GMPID":"1:617308524857:web:73cf4f7fd16f659626d8e8"},"messageUrlParams":{"database":"projects/sxxxxxxxx/databases/(default)"},"sendRawJson":true,"supportsCrossDomainXhr":true,"internalChannelParams":{"forwardChannelRequestTimeoutMs":600000},"forceLongPolling":false,"detectBufferingProxy":false,"xmlHttpFactory":{"l":null,"j":false},"encodeInitMessageHeaders":true}
 LOG  [2023-03-11T12:47:15.708Z]  @firebase/firestore: Firestore (9.17.2): FirebaseAppCheckTokenProvider AppCheck not yet detected
 LOG  [2023-03-11T12:47:15.712Z]  @firebase/firestore: Firestore (9.17.2): Connection Opening WebChannel transport.
 LOG  [2023-03-11T12:47:15.714Z]  @firebase/firestore: Firestore (9.17.2): Connection WebChannel sending: {"database":"projects/xxxxxx/databases/(default)","addTarget":{"query":{"structuredQuery":{"from":[{"collectionId":"users"}],"orderBy":[{"field":{"fieldPath":"__name__"},"direction":"ASCENDING"}]},"parent":"projects/xxxxxxxxxxx/databases/(default)/documents"},"targetId":2}}
 LOG  [2023-03-11T12:47:16.132Z]  @firebase/firestore: Firestore (9.17.2): Connection WebChannel transport opened.
 ERROR  [2023-03-11T12:47:25.695Z]  @firebase/firestore: Firestore (9.17.2): Could not reach Cloud Firestore backend. Backend didn't respond within 10 seconds.
This typically indicates that your device does not have a healthy Internet connection at the moment. The client will operate in offline mode until it is able to successfully connect to the backend.
 LOG  [2023-03-11T12:47:25.747Z]  @firebase/firestore: Firestore (9.17.2): MemoryPersistence Starting transaction: Release target
 LOG  [2023-03-11T12:47:25.749Z]  @firebase/firestore: Firestore (9.17.2): Connection WebChannel sending: {"database":"projects/xxxxxxxxx/databases/(default)","removeTarget":2}

If I run the same code on react in the browser on my Mac it works,
If I run my react code on the iOS simulator in the browser it also works.

if I run it in react native on iOS simulator the timeout appears.
If I try to add force long polling as suggested here, I get a different error: #6993 (comment)

 LOG  Running "mobile" with {"rootTag":21,"initialProps":{}}
 LOG  [2023-03-11T12:55:07.861Z]  @firebase/firestore: Firestore (9.17.2): FirebaseAuthCredentialsProvider Auth not yet detected
 LOG  [2023-03-11T12:55:07.863Z]  @firebase/firestore: Firestore (9.17.2): FirestoreClient Using default OnlineComponentProvider
 LOG  [2023-03-11T12:55:07.863Z]  @firebase/firestore: Firestore (9.17.2): FirestoreClient Using default OfflineComponentProvider
 LOG  [2023-03-11T12:55:07.864Z]  @firebase/firestore: Firestore (9.17.2): FirestoreClient Initializing OfflineComponentProvider
 LOG  [2023-03-11T12:55:07.867Z]  @firebase/firestore: Firestore (9.17.2): FirestoreClient Initializing OnlineComponentProvider
 LOG  [2023-03-11T12:55:07.873Z]  @firebase/firestore: Firestore (9.17.2): MemoryPersistence Starting transaction: Allocate target
 LOG  [2023-03-11T12:55:07.881Z]  @firebase/firestore: Firestore (9.17.2): MemoryPersistence Starting transaction: Execute query
 LOG  [2023-03-11T12:55:07.885Z]  @firebase/firestore: Firestore (9.17.2): QueryEngine Using full collection scan to execute query: Query(target=Target(users, orderBy: [__name__ (asc)]); limitType=F)
 LOG  [2023-03-11T12:55:07.897Z]  @firebase/firestore: Firestore (9.17.2): Connection Creating WebChannel: https://firestore.googleapis.com/google.firestore.v1.Firestore/Listen/channel {"httpSessionIdParam":"gsessionid","initMessageHeaders":{"X-Goog-Api-Client":"gl-js/ fire/9.17.2","Content-Type":"text/plain","X-Firebase-GMPID":"1:617308524857:web:73cf4f7fd16f659626d8e8"},"messageUrlParams":{"database":"projects/xxxxxx/databases/(default)"},"sendRawJson":true,"supportsCrossDomainXhr":true,"internalChannelParams":{"forwardChannelRequestTimeoutMs":600000},"forceLongPolling":true,"detectBufferingProxy":false,"xmlHttpFactory":{"l":null,"j":false},"encodeInitMessageHeaders":true}
 LOG  [2023-03-11T12:55:07.902Z]  @firebase/firestore: Firestore (9.17.2): FirebaseAppCheckTokenProvider AppCheck not yet detected
 LOG  [2023-03-11T12:55:07.905Z]  @firebase/firestore: Firestore (9.17.2): Connection Opening WebChannel transport.
 LOG  [2023-03-11T12:55:07.908Z]  @firebase/firestore: Firestore (9.17.2): Connection WebChannel sending: {"database":"projects/xxxxxxx/databases/(default)","addTarget":{"query":{"structuredQuery":{"from":[{"collectionId":"users"}],"orderBy":[{"field":{"fieldPath":"__name__"},"direction":"ASCENDING"}]},"parent":"projects/xxxxxx/databases/(default)/documents"},"targetId":2}}
 LOG  [2023-03-11T12:55:08.196Z]  @firebase/firestore: Firestore (9.17.2): Connection WebChannel transport opened.
 LOG  [2023-03-11T12:55:08.384Z]  @firebase/firestore: Firestore (9.17.2): Connection WebChannel received: {"targetChange":{"targetChangeType":"ADD","targetIds":[2]}}
 LOG  [2023-03-11T12:55:08.385Z]  @firebase/firestore: Firestore (9.17.2): Connection WebChannel received: {"documentChange":{"document":{"name":"projects/xxxxxx/databases/(default)/documents/users/MlqKe5zqg2gxcKZ6ey10Q02p3OH2","fields":{"status":{"stringValue":"APPROVED"},"details":{"mapValue":{"fields":{"address":{"mapValue":{"fields":{"countryCode":{"nullValue":null},"addressLine3":{"nullValue":null},"addressLine1":{"nullValue":null},"postCode":{"nullValue":null},"addressLine2":{"nullValue":null}}}},"notes":{"nullValue":null},"createdAt":{"timestampValue":"2023-03-07T15:19:02.121Z"}},"createTime":"2023-03-07T15:19:02.147834Z","updateTime":"2023-03-08T15:15:12.650778Z"},"targetIds":[2]}}
 LOG  [2023-03-11T12:55:08.385Z]  @firebase/firestore: Firestore (9.17.2): Connection WebChannel received: {"targetChange":{"targetChangeType":"CURRENT","targetIds":[2],"resumeToken":"xxxxxxx","readTime":"2023-03-11T12:55:08.162951Z"}}
 LOG  [2023-03-11T12:55:08.385Z]  @firebase/firestore: Firestore (9.17.2): Connection WebChannel received: {"targetChange":{"resumeToken":"xxxxxxxx=","readTime":"2023-03-11T12:55:08.162951Z"}}
 LOG  [2023-03-11T12:55:08.386Z]  @firebase/firestore: Firestore (9.17.2): Connection WebChannel received: {"filter":{"targetId":2,"count":1}}
 LOG  [2023-03-11T12:55:08.386Z]  @firebase/firestore: Firestore (9.17.2): Connection WebChannel received: {"targetChange":{"resumeToken":"CgkIz5PYmfbT/QI=","readTime":"2023-03-11T12:55:08.272079Z"}}
 WARN  Possible Unhandled Promise Rejection (id: 0):
ReferenceError: Property 'DOMException' doesn't exist
ReferenceError: Property 'DOMException' doesn't exist
    at anonymous (http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false&modulesOnly=false&runModule=true&app=xxxxxx:98994:32)
    at fromBase64String (http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false&modulesOnly=false&runModule=true&app=xxxxxx:98996:10)

if I build the iOS app with Hermes (https://hermesengine.dev/) disabled, same thing, doesn't work
if I build the iOS app with Hermes (https://hermesengine.dev/) disabled and debug through my chrome and add force long polling=> it works

Current workaround for me is:
use forceLongPolling as stated here: #6993 (comment)
and add:

import {decode, encode} from 'base-64';
global.DOMException = function DOMException(message, name) {
    console.log(message, name);
  };

  if (!global.btoa) {
    global.btoa = encode;
  }

  if (!global.atob) {
    global.atob = decode;
  } 

Steps to reproduce:

Try run the following code on react-native with iOS simulator

  • Browser version: react-native 0.71.3
  • Firebase SDK version: 9.17.2
  • Firebase Product: firestore

Relevant Code:

import React, {useRef, createRef, useEffect} from 'react';
import {Text, View} from 'react-native';
import {initializeApp} from 'firebase/app';
import {initializeFirestore, collection, getDocs} from 'firebase/firestore';

const firebaseConfig = {
 xxxxx
};

const App = () => {
  // Initialize Firebase
  const app = initializeApp(firebaseConfig);

  // Initialize Cloud Firestore and get a reference to the service
  const db = initializeFirestore(app, {
    experimentalForceLongPolling: true, // comment this out to reproduce error
  });
  getDocs(collection(db, 'users'))
    .then((data) => {
      data.forEach((doc) => {
        console.log(`${doc.id} => ${doc.data()}`);
      });
    })
    .catch((err) => {
      console.log(err);
    });
  return (
    <View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
      <Text>Hello world</Text>
    </View>
  );
};

export {App};

Conclusion

Using firebase js SDK with React native currently only seems to work if you use "experimentalForceLongPolling"
However, this leads to another error, because atob and DOMException are not present in the global node runtime

I am guessing here, but either this is due to React-Native 0.71.3 or some changes to firebase js SDK

@superphil0
Copy link
Author

Looks like this is a regression of #2667

@jbalidiong jbalidiong added the v9 label Mar 13, 2023
@MarkDuckworth MarkDuckworth self-assigned this Mar 13, 2023
@dconeybe
Copy link
Contributor

The culprit of the DOMException problem is #7019, which added these lines into packages/firestore/src/platform/browser/base64.ts:

if (e instanceof DOMException) {
throw new Base64DecodeError('Invalid base64 string: ' + e);
} else {
throw e;
}

I had no idea that using DOMException would be problematic.

It looks the code should verify that typeof DOMException !== 'undefined' before actually using DOMException:

typeof DOMException !== 'undefined' &&
error instanceof DOMException

This bug was introduced in v9.17.2 (released March 2, 2023: https://firebase.google.com/support/release-notes/js#version_9172_-_march_2_2023) so downgrading to the previous version, 9.17.1, should fix this. I'll try to get the fix in the next release.

As for the "Could not reach Cloud Firestore backend", enabling long polling is our typical initial recommendation when this problem is faced.

Can you try out v9.17.1 and report back your experience?

@dconeybe
Copy link
Contributor

The strange thing is that Firestore implements special logic for react-native for base64 decoding that does not use atob() or DOMException: https://github.com/firebase/firebase-js-sdk/blob/a8be6ed527b4b5e8f46cc7badaa3a2171cf36327/packages/firestore/src/platform/rn/base64.ts

So why is Firestore trying to use the browser logic in https://github.com/firebase/firebase-js-sdk/blob/a8be6ed527b4b5e8f46cc7badaa3a2171cf36327/packages/firestore/src/platform/browser/base64.ts to decode the base64 string? This sounds like a bundling/packaging issue.

Even if I fix this DOMExeception issue, there is still the issue that a react-native app is incorrectly trying to use atob().

@superphil0 Are you able to share your project so I can look at its structure? If not, could you share just the skeleton of the project, with the source code removed?

@dconeybe
Copy link
Contributor

Ok I've merged #7130 to fix the ReferenceError: Property 'DOMException' doesn't exist issue. But this will probably just uncover the original exception that was masked, which is likely that atob() is not defined.

@superphil0 Please provide a project that we can use to reproduce and we'll see if we can figure out why the react-native-specific base64 decoding logic isn't being used.

@MarkDuckworth
Copy link
Contributor

@superphil0, the app should be using the react-native bundle, but it appears to be using the browser bundle. Look for what could cause that. Or as @dconeybe suggested, provide a project reproducing the error so we can investigate further.

@superphil0
Copy link
Author

I downgraded to 9.17.1, this fixes the DOMException, but unmasks atob.

I created a reproduction repo and sent an invite to @dconeybe

@dconeybe
Copy link
Contributor

Thank you for the app, @superphil0. I've been able to reproduce and I've confirmed that, for some reason, your app is loading the index.esm2017.js bundle (intended for use in the browser) instead of the index.rn.js bundle (intended for use in react-native apps). I don't know enough about react-native to guess at the root cause. Did you do any "customizations" to react-native's bundler which could cause it to use the non-react-native-specific bundle?

@dconeybe
Copy link
Contributor

@superphil0 I found this little suggestion about using metro with firebase: https://docs.expo.dev/guides/using-firebase/#configure-metro. I tried to do this with your app but it didn't seem to have any effects. Namely, after the line

const {resolver: defaultResolver} = getDefaultConfig.getDefaultValues();

I added a new line, becoming:

const {resolver: defaultResolver} = getDefaultConfig.getDefaultValues();
defaultResolver.assetExts.push('cjs');

This didn't seem to have any effects though. Since I know nothing about react-native, I'm not sure there is much else I can do to help. The key is to figure out why your react-native project fails to import the react-native bundle from firestore. Can you dig into this a little?

@MarkDuckworth
Copy link
Contributor

@superphil0, can you give me and @dwyfrequency access to the repro?

@MarkDuckworth
Copy link
Contributor

@superphil0, there may be some information on this page that will help you fix the import.

@superphil0
Copy link
Author

superphil0 commented Mar 20, 2023

Hey, first, thank you for taking a look.

I don't think the links you mentioned are relevant, since they are about expo, however we use react native.

I will look at this next week as i am traveling currently. I think the app loading the wrong bundle might be the right direction. I inherited this project, so it's worth creating a react native project from scratch and trying to reproduce, however when i tried this i ran into a buggy version of react native init...

@MarkDuckworth @dwyfrequency i added you

@MarkDuckworth
Copy link
Contributor

I can't build your repo. But you might want to look at resolverMainFields. This field seems to specify which package entry points the metro bundler will use. In some cases, this will prefer the browser bundle.

@dconeybe dconeybe removed their assignment Mar 21, 2023
@MarkDuckworth
Copy link
Contributor

I still was unable to build the repo, likely issues on my machine. However, the repo indicates you are using the metro bundler. Can you test setting resolverMainFields: ['react-native', 'browser', 'main'] in your metro.config.js file?

@superphil0
Copy link
Author

I can confirm this solves our issue with the firebase sdk. Thank you so much for your help

@hsubox76
Copy link
Contributor

Just wanted to leave an explanation for anyone else having this issue. This project's metro config added some custom options to resolver but did it by spreading defaultResolver first:

  resolver: {
    ...defaultResolver,
    sourceExts: [...defaultResolver.sourceExts, 'cjs'],
    blacklistRE: exclusionList([/cdk-app\/.*/, /api-app\/.*/]),
  },

Metro's default resolver settings actually look at mainFields ["browser", main"] because Metro is used in non-React Native projects as well and they wanted to keep the defaults more generic. When you create a React Native project with the React Native CLI, the "react-native" field gets add to the beginning of that, but when you put defaultResolver back into the resolver, it overrides that and sets it back to the generic settings. See facebook/metro#807 for an example.

Their solution is just don't spread defaultResolver. Looks like it's not necessary, anything specified in the resolver field will extend defaults I guess, so you don't have to fully fill it out.

@firebase firebase locked and limited conversation to collaborators Apr 28, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

6 participants