firebase / firebase-js-sdk

Firebase Javascript SDK
https://firebase.google.com/docs/web/setup
Other
4.83k stars 892 forks source link

Can't use Firestore emulator from React Native mobile emulators (iOS and Android) #7530

Closed KishiTheMechanic closed 1 year ago

KishiTheMechanic commented 1 year ago

Operating System

iOS 16.4, Android 13

Browser Version

Safari/604.1

Firebase SDK Version

9.17.2

Firebase SDK Product:

Firestore

Describe your project's tooling

{
  "dependencies": {
    "@expo-google-fonts/noto-sans-jp": "0.2.3",
    "@expo-google-fonts/outfit": "0.2.3",
    "@react-native-async-storage/async-storage": "1.17.11",
    "@react-native-community/cli-platform-android": "10.2.0",
    "@react-native-community/cli-platform-ios": "10.2.0",
    "@react-navigation/native": "6.1.6",
    "@react-navigation/native-stack": "6.9.12",
    "@rivascva/react-native-code-editor": "1.2.2",
    "@skeet-framework/firestore": "1.0.9",
    "clsx": "1.2.1",
    "date-fns": "2.30.0",
    "dotenv": "16.0.3",
    "expo": "48.0.5",
    "expo-checkbox": "2.3.1",
    "expo-font": "11.1.1",
    "expo-image": "1.0.1",
    "expo-image-picker": "14.1.1",
    "expo-linking": "4.0.1",
    "firebase": "9.17.2",
    "framer-motion": "10.3.4",
    "i18next": "22.4.10",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "react-i18next": "12.2.0",
    "react-native": "0.71.8",
    "react-native-gesture-handler": "2.9.0",
    "react-native-heroicons": "3.2.0",
    "react-native-popup-menu": "0.16.1",
    "react-native-reanimated": "2.14.4",
    "react-native-safe-area-context": "4.5.0",
    "react-native-screens": "3.20.0",
    "react-native-svg": "13.4.0",
    "react-native-svg-transformer": "1.0.0",
    "react-native-toast-message": "2.1.6",
    "react-native-utilities": "0.1.7",
    "react-native-web": "0.18.12",
    "recoil": "0.7.7",
    "text-encoding": "0.7.0",
    "twrnc": "3.6.1",
    "typesaurus": "7",
    "zod": "3.21.4"
  },
  "devDependencies": {
    "@babel/core": "7.21.0",
    "@expo/metro-config": "0.7.1",
    "@expo/webpack-config": "18.0.1",
    "@svgr/webpack": "6.5.1",
    "@types/jest": "29.5.0",
    "@types/node": "18.16.0",
    "@types/node-fetch": "2.6.2",
    "@types/react": "18.0.28",
    "@types/react-dom": "18.0.11",
    "@types/text-encoding": "0.0.36",
    "@typescript-eslint/eslint-plugin": "5.54.0",
    "@typescript-eslint/parser": "5.54.0",
    "babel-loader": "9.1.2",
    "babel-plugin-module-resolver": "5.0.0",
    "eas-cli": "3.7.2",
    "esbuild": "0.17.14",
    "eslint": "8.36.0",
    "eslint-config-prettier": "8.8.0",
    "eslint-plugin-react": "7.32.2",
    "eslint-plugin-react-hooks": "4.6.0",
    "eslint-plugin-react-native": "4.0.0",
    "firebase-tools": "12.0.1",
    "jest": "29.3.1",
    "nodemon": "2.0.22",
    "npm-check-updates": "16.8.0",
    "npm-run-all": "4.1.5",
    "prettier": "2.8.8",
    "prettier-plugin-tailwindcss": "0.2.4",
    "tailwindcss": "3.3.1",
    "ts-jest": "29.0.5",
    "ts-loader": "9.4.2",
    "tsconfig-paths": "4.1.2",
    "typescript": "4.9.4"
  }
}

Describe the problem

I could use the Auth emulator correctly but not the Firestore emulator from React Native (Expo) with iOS and Android emulators on my Mac.

the settings are like this:

import firebaseConfig from '@lib/firebaseConfig'
import { getAnalytics } from 'firebase/analytics'
import { initializeApp, getApp, getApps } from 'firebase/app'
import { connectAuthEmulator, getAuth } from 'firebase/auth'
import { getStorage, connectStorageEmulator } from 'firebase/storage'
import { getFirestore, connectFirestoreEmulator } from 'firebase/firestore'
import { Platform } from 'react-native'

export const firebaseApp = !getApps().length
  ? initializeApp(firebaseConfig)
  : getApp()

const getFirebaseAuth = () => {
  const firebaseAuth = getAuth(firebaseApp)
  if (process.env.NODE_ENV !== 'production' && Platform.OS === 'web') {
    connectAuthEmulator(firebaseAuth, 'http://127.0.0.1:9099', {
      disableWarnings: true,
    })
  }
  if (process.env.NODE_ENV !== 'production' && Platform.OS === 'android') {
    connectAuthEmulator(firebaseAuth, 'http://10.0.2.2:9099', {
      disableWarnings: true,
    })
  }
  if (process.env.NODE_ENV !== 'production' && Platform.OS === 'ios') {
    connectAuthEmulator(firebaseAuth, 'http://0.0.0.0:9099', {
      disableWarnings: true,
    })
  }
  return firebaseAuth
}

export const auth = firebaseApp ? getFirebaseAuth() : undefined

const getFirebaseStorage = () => {
  const firebaseStorage = getStorage(firebaseApp)
  if (process.env.NODE_ENV !== 'production' && Platform.OS === 'web') {
    connectStorageEmulator(firebaseStorage, '127.0.0.1', 9199)
  }
  if (process.env.NODE_ENV !== 'production' && Platform.OS === 'android') {
    connectStorageEmulator(firebaseStorage, '10.0.2.2', 9199)
  }
  if (process.env.NODE_ENV !== 'production' && Platform.OS === 'ios') {
    connectStorageEmulator(firebaseStorage, '0.0.0.0', 9199)
  }
  return firebaseStorage
}

export const storage = firebaseApp ? getFirebaseStorage() : undefined

const getFirebaseFirestore = () => {
  const firestoreDb = getFirestore(firebaseApp)
  if (process.env.NODE_ENV !== 'production' && Platform.OS === 'web') {
    connectFirestoreEmulator(firestoreDb, '127.0.0.1', 8080)
  }
  if (process.env.NODE_ENV !== 'production' && Platform.OS === 'android') {
    connectFirestoreEmulator(firestoreDb, '10.0.2.2', 8080)
  }
  if (process.env.NODE_ENV !== 'production' && Platform.OS === 'ios') {
    connectFirestoreEmulator(firestoreDb, '0.0.0.0', 8080)
  }
  return firestoreDb
}

export const db = firebaseApp ? getFirebaseFirestore() : undefined

export const analytics =
  typeof window !== 'undefined' &&
  process.env.NODE_ENV === 'production' &&
  firebaseApp
    ? getAnalytics(firebaseApp)
    : undefined

I'm getting these errors when I use firestore:

FirebaseError: 
false for 'get' @ L5
FirebaseError: [code=permission-denied]: 
false for 'get' @ L5

 ERROR  [TypeError: Network request failed]
 ERROR  [TypeError: Cannot read property 'status' of undefined]

Please tell me how to use the Firestore emulator from mobile emulators (iOS and Android) with React Native(Expo).

Thank you always.

Steps and code to reproduce issue

Please use this repository to reproduce issue.

https://github.com/elsoul/skeet-app

I'm using Expo: https://expo.dev/

MarkDuckworth commented 1 year ago

What rules are configured for Firestore in the emulator? You can see this through the emulator UI.

KishiTheMechanic commented 1 year ago

I appreciate your support.

I'm putting firestore.rules on the project rules.

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /User/{userId}/{document=**} {
      allow read: if request.auth != null;
      allow write: if request.auth.uid == userId;
    }
  }
}

I couldn't find the rules on the emulator. Can I set the rule file path or something in somewhere like firebase.json?

The error looks like exactly when the rule is not affected...

Screenshot 2023-08-08 at 16 43 04 Screenshot 2023-08-08 at 16 43 12
KishiTheMechanic commented 1 year ago

I found new error logs for this on the log from React Native, so sharing them.

 ERROR  [2023-08-08T14:51:40.430Z]  @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.
 WARN  Possible Unhandled Promise Rejection (id: 0):
FirebaseError: Failed to get document because the client is offline.
FirebaseError: [code=unavailable]: Failed to get document because the client is offline.
 WARN  Possible Unhandled Promise Rejection (id: 1):
FirebaseError: Failed to get document because the client is offline.
FirebaseError: [code=unavailable]: Failed to get document because the client is offline.
 WARN  Possible Unhandled Promise Rejection (id: 2):
FirebaseError: Failed to get document because the client is offline.
FirebaseError: [code=unavailable]: Failed to get document because the client is offline.

Thank you always. Have a great day☀

KishiTheMechanic commented 1 year ago

My firebase.json is this:

{
  "functions": [
    {
      "source": "functions/openai",
      "codebase": "openai",
      "ignore": [
        "node_modules",
        ".git",
        "firebase-debug.log",
        "firebase-debug.*.log"
      ]
    }
  ],
  "firestore": {
    "rules": "firestore.rules",
    "indexes": "firestore.indexes.json"
  },
  "storage": {
    "rules": "storage.rules"
  },
  "emulators": {
    "auth": {
      "port": 9099
    },
    "functions": {
      "port": 5001
    },
    "firestore": {
      "port": 8080
    },
    "pubsub": {
      "port": 8085
    },
    "hosting": {
      "port": 8000
    },
    "storage": {
      "port": 9199
    },
    "ui": {
      "enabled": true
    },
    "singleProjectMode": true
  }
}
KishiTheMechanic commented 1 year ago

Thank you so much for Firebase support team, I got some issues similar from this and Firestore problem was solved with:

  const firestoreDb = initializeFirestore(firebaseApp, {
    experimentalForceLongPolling: true,
  })

Instead of using

const firestoreDb = getFirestore(firebaseApp)

initializeFirestore solved the problem with Android development.

However,

 ERROR  [TypeError: Network request failed]
 ERROR  [TypeError: Cannot read property 'status' of undefined]

These errors still exist. Do we need to use 10.2.2.0 for Firebase Functions as well?

KishiTheMechanic commented 1 year ago

Ohhhhhh it finally works well!!

We need to change the emulator host by the platform.

          platform === 'web'
              ? '127.0.0.1'
              : platform === 'android'
              ? '10.0.2.2'
              : '0.0.0.0'

Now it's working on the Android too. It's so nice!!!!🙌

https://github.com/elsoul/skeet-solana-mobile-stack It's now working with the emulators🚀🔥

Thank you for your support always!

Have a great day☀

MarkDuckworth commented 1 year ago

I'm glad you figured it out and I love the enthusiasm. I'll let the team know that long polling fixed this issue with connecting to the Firestore emulator.

Also, a note to future readers, the last comment is a fix for the issue connecting to the functions emulator.

KishiTheMechanic commented 1 year ago

Yes, thank you for your note.

The way to call Firebase Functions looks like this as a side note.

const callFirebaseFunctions = async () => {
  const platformDevIP =
  Platform.OS === 'web'
    ? '127.0.0.1'
    : Platform.OS === 'android'
    ? '10.0.2.2'
    : '0.0.0.0'
   const url =
      process.env.NODE_ENV === 'production'
        ? `https://${
            functionDomain
          }/${functionName}/${toKebabCase(methodName)}`
        : `http://${platformDevIP}:5001/${projectId}/${region}/${methodName}`
    const fbToken = await auth?.currentUser?.getIdToken()
    const res = await fetch(`${url}`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${fbToken}`,
      },
      body: JSON.stringify(params),
    })
    return res
 }