【Firebase】エミュレーターの忘備録

# 忘備録の趣旨

僕は普段、仕事のプロジェクトではReact NativeとFirebaseを使って開発を行っています。

React NativeとFirebaseを使って開発を行うときは、FirebaseのエミュレーターであるFirebase Local EmulatorSuiteを使って開発を行います。

この記事では、どのようにローカルでエミュレータを起動しているかと、どのようにアプリからエミュレータに接続するのかを、将来の自分のためにまとめようと思います。

# Firebase Local EmulatorSuiteを起動する手順

まずは、プロジェクトをビルドします。

```shell
yarn build
```

`GOOGLE_APPLICATION_CREDENTIALS`環境変数に、サービスアカウントファイルのパスを指定しながら、`firebase emulators:start`コマンドを実行してエミュレータを起動します。

```shell
GOOGLE_APPLICATION_CREDENTIALS='./path/to/serviceAccountKey.json' firebase emulators:start
```

## ポートが開いていないためエミュレーターが起動できない場合の解決方法

起動中のエミュレーターを正常に終了できなかった後に、再度エミュレーターを上記の手順で起動しようとすると次のようなエラーが発生することがあります。

```shell
i  emulators: Starting emulators: auth, functions, firestore, database, hosting, pubsub, storage
⚠  firestore: Port 8080 is not open on localhost (127.0.0.1,::1), could not start Firestore Emulator.
⚠  firestore: To select a different host/port, specify that host/port in a firebase.json config file:
```

これは、Port 8080が開いていないため、Firestoreエミュレーターを起動できないということを表しています。

これを解決するためには、`kill`コマンドを使用して、ポート番号8080を使用中のプロセスを強制的に終了させる必要があります。

ポート番号8080（Firestore Emulator）で問題が起きている場合は次のコマンドを実行します。

```shell
kill -9 $(lsof -t -i:8080)
```

ポート番号8085（Pub/Sub Emulator）で問題が起きている場合は次のコマンドを実行します。

```shell
kill -9 $(lsof -t -i:8085)
```

ポート番号9000（Database Emulator）で問題が起きている場合は次のコマンドを実行します。

```shell
kill -9 $(lsof -t -i:9000)
```


# React Nativeからエミュレータに接続する方法

最初に`initializeApp()`を呼び出して、`FirebaseApp`のインスタンスを取得します。

```typescript
const firebase = initializeApp({
    projectId: "project-id",
    apiKey: "api-key",
    authDomain: "auth-domein",
    storageBucket: "storage-bucket"
});
```

Auth Emulatorに接続する場合は次のようにします。

```typescript
const auth = getAuth(firebase);

if (isAndroid) {
    connectAuthEmulator(auth, "http://10.0.x.x:9099");
} else {
    connectAuthEmulator(auth, "http://localhost:9099");
}
```

Firestore Emulatorに接続する場合は次のようにします。

```typescript
const firestore = getFirestore(firebase);

if (isAndroid) {
    connectFirestoreEmulator(firestore, "10.0.x.x", 8080)
} else {
    connectFirestoreEmulator(firestore, "localhost", 8080)
}
```

Functions Emulatorに接続する場合は次のようにします。

```typescript
export const functions = getFunctions(firebase, REGION);

if (isAndroid) {
    connectFunctionsEmulator(functions, "10.0.x.x", 5001);
} else {
    connectFunctionsEmulator(functions, "localhost", 5001);
}
```

あとは、以下のコマンドを実行して、React Native（Expo）アプリを起動するだけです。

```shell
expo start --clear
```

# Androidの時だけ"localhost"ではなくIPアドレスを指定する理由

上記のコードでは、**Android**エミュレーターで実行している時にだけ直接IPアドレスを渡してエミュレーターに接続する関数を呼び出しています。

これは、Androidのエミュレーター環境では、"localhost"が解決しないことが多々ありエミュレーターに接続できないという問題が僕の環境ではよく発生していました。

そのため、その問題を解決するために直接IPアドレスを指定しています。

# IPアドレスを確認する方法（Mac編）

開発環境のIPアドレスを確認する方法を紹介します。今回はMacでの場合のみ紹介させていただきます。あらかじめご了承ください。

設定アプリを開いて、左のサイドメニューからネットワークを選択します。

![名称未設定のデザイン (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/577119/31d0284e-f437-9a5d-99a5-5e1fa46468c7.png)

**Wi-Fi**をクリックします。

接続されているWi-Fiの詳細ボタンをクリックします。

![名称未設定のデザイン (4).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/577119/b3f48420-6070-2078-f84b-b989aa74bfec.png)

このIPアドレスと表示されているが、エミュレーターに接続する際に使用可能なIPアドレスです。

# まとめ

Firebase Local EmulatorSuiteを起動をするときに、何度も手打ちしたりコピペしたりするのは大変だと思うので、`package.json`の`scripts`にスクリプトとして登録しておくことをお勧めします。

```json
"scripts": {
    "start:emulators": "yarn build && GOOGLE_APPLICATION_CREDENTIALS='./path/to/secretKey.json' firebase emulators:start",
    ...
}
```