6

My problem is that I'm unable to call firestore from the client using the firebase firestore emulator on Android emulators. I've tested multiple Android emulators.

The error I'm getting suggests the app is unable to connect to firestore at all claiming there's not internet connection. Here's the full message: "@firebase/firestore:, Firestore (7.8.1): Could not reach Cloud Firestore backend. Connection failed 1 times. Most recent error: FirebaseError: [code=unavailable]: The operation could not be completed 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."

This confuses me because my app works with the firebase firestore emulator on iOS. It also still works with the firebase cloud functions emulator (Nevermind. It was never working with cloud functions) for android. In case it matters, the app also works fine on iOS and Android when using firestore in production.

I'm creating a React Native app using the Expo managed workflow so I'm using firebase's web SDK. This means I'd have to eject to switch to react-native-firebase.

I've also looked at the docs and I seem to be following the instructions there: https://firebase.google.com/docs/emulator-suite/connect_firestore#web

I also came across this issue and I'm not clear if they're related. https://github.com/firebase/firebase-js-sdk/issues/2923 If they are I'm also not sure how I could fix it while remaining on the expo managed workflow.

Here's my firebase config

firebaseConfig.js

import * as firebase from 'firebase';
// Optionally import the services that you want to use
//import "firebase/auth";
//import "firebase/database";
import "firebase/firestore"; // uncommented so I could test locally w/ emulator
import "firebase/functions";
//import "firebase/storage";

// ios id 
  // appId: "1:586841249272:ios:d8b508f7811d7c84e0b20d",

// Initialize Firebase
const firebaseConfig = {
  apiKey: "myApikey",
  authDomain: "fsp2-a670d.firebaseapp.com",
  databaseURL: "https://fsp2-a670d.firebaseio.com",
  projectId: "fsp2-a670d",
  storageBucket: "fsp2-a670d.appspot.com",
  messagingSenderId: "586841249272",
  appId: "1:586841249272:android:fa68525699ea5cdde0b20d"
};

// added .firestore to test firestore locally w/ emulator 
const db = firebase.initializeApp(firebaseConfig).firestore(); 

// for debugging
firebase.firestore.setLogLevel('debug')

// Uncomment the below line to use cloud functions with the emulator
firebase.functions().useFunctionsEmulator('http://localhost:5001')
// firebase.firestore().settings({ experimentalForceLongPolling: true });

// uncomment this to test firestore locally w/ emulator 
  db.settings({
    host: "localhost:8080",
    ssl: false
  });

And here's the code in the file where the call to firestore from the client fails. It does

const More = ({navigation}) => {
  const [userInfo, setUserInfo] = useState({profilePicture: 'placeholder', userName: ''});

  useEffect(() => {
    const changeUserInfo = async () => {
      const userData = await new Promise(function (resolve, reject) {
        // 2 - Copy-paste your code inside this function
        firebase.auth().onAuthStateChanged(user => {
          resolve(user) // this promise is rejected and I need to write the code to handle rejections
        })
      })
      const db = firebase.firestore();
      const uid = userData?.uid;
      if (uid) {
        const userRef = await db.collection('users').doc(uid);
        const user = await userRef.get();
        const userFields = user.data();
        console.log('userFields is: ', userFields);
        const {profilePicture, userName} = userFields;
        console.log('profilePicture is: ', profilePicture);
        console.log('userName is: ', userName);
        setUserInfo(() => {
          return {
            profilePicture,
            userName,
          }
        });
      }
    }
    changeUserInfo()
  }, [userInfo.profilePicture, userInfo.userName]

I'd really appreciate any help!

Gwater17
  • 2,018
  • 2
  • 19
  • 38

3 Answers3

6

In db.settings I needed to set host to "10.0.2.2:8080"

To get cloud functions to work I needed to replace firebase.functions().useFunctionsEmulator('http://localhost:5001') with firebase.functions().useFunctionsEmulator('http://10.0.2.2:5001')

10.0.2.2 is the special IP address to connect to the 'localhost' of the host computer from an Android emulator. https://firebase.google.com/docs/emulator-suite/connect_and_prototype#android_1

Gwater17
  • 2,018
  • 2
  • 19
  • 38
  • 1
    Thank you! It seems obvious now in hindsight how localhost wouldn't work on an emulator/device. In my case, when using a real phone, I needed to use the IP of my computer instead of 10.0.2.2. – Cezille07 Apr 08 '21 at 06:49
  • Do you know how to get realtime database to work? – peter Aug 25 '21 at 01:32
  • @petermir realtimeDatabase.useEmulator('10.0.2.2', 9000) – Gwater17 Aug 26 '21 at 15:01
0

I managed to solve this by using Expo's built-in variable of the debugger host, which works for physical iOS devices as well: Constants.manifest.debuggerHost?.split(":").shift().

Wrote up a short explanation on that here:

https://dev.to/haydenbleasel/using-the-firebase-local-emulator-with-expo-s-managed-workflow-5g5k

Dharman
  • 30,962
  • 25
  • 85
  • 135
0

Although my own case was not relatable to any JS library/framework but rather while using vanilla JavaScript, I ran into the same issue using the firebase Emulator on my local machine.

After some Googling without any hope in site, I discovered that by setting the firestore url and disabling SSL after initailizing firebase solved my own problem. Example below:

             /** After: const firestore = firebase.initializeApp("<firebase-config>").firestore() **/

                firestore.settings({
                    host: "localhost:8080",
                    ssl: false
                });
Dev
  • 411
  • 3
  • 6