【第１回】何人が何十回やってるか分からないが、それでも俺はFirebaseでチャットアプリを作りたい

# はじめに
Firebaseって最高ですよね！サーバーサイドやインフラ周りのことを極力考えずにクライアント側の開発に集中できるのは、アプリ開発者にとってはこの上ない幸せだと思います。
今回はそんなFirebaseを使った簡単なチャットアプリの作り方を3回に渡って説明していきます。第一回目はFirebaseのセットアップとログイン処理を実装します。
全体のソースコードは以下です。
https://github.com/maru01/MyChat


## やること
- 何人が何十回やってるか分からない擦り倒されたネタ「Firebaseでチャットアプリを作る」をやります。
- OSはAndroidです。
- 使用するFirebaseのサービスはFirestoreのみです。

### アプリの仕様
- ユーザーは名前とパスワードを入力することによりログインできます。
- 同じ名前のユーザーを作ることはできません。
- 任意のユーザーには、パスワードを知っていれば誰でもログインできます。
- ユーザーはチャット部屋を作ることができます。
- チャット部屋内では自分のチャットと他人のチャットを区別できます。

## やらないこと
- Firebaseの説明
- セキュリティ周り。無視して進めます。実際のプロダクトではセキュリティルールをしっかり定義してユーザーがRead/Writeできるデータを限定する必要があります。<br>
https://firebase.google.com/docs/firestore/security/get-started?hl=ja
- アーキテクチャ周り。MVVMで綺麗に書きたいところですが本質ではないためこだわりません。

# 本題の前に
Qiita初投稿のため至らない点が数多くあるかと思います。温かく見守っていただけると幸いです。
では本題。

# Firebaseのセットアップ
Firebaseのセットアップは簡単です。
FirebaseコンソールにログインしてFirebaseプロジェクトを作ります。完了したら「開始するにはアプリを追加してください」の上のドロイド君アイコンをクリックします。
<img width="1000" alt="スクリーンショット 2018-12-03 17.46.51.png" src="https://qiita-image-store.s3.amazonaws.com/0/277406/6b4f11e0-51ec-6ffa-6828-f440b88be68f.png">
↓のような画面が表示されるので指示に従い入力項目を埋めていきます。
<img width="1260" alt="スクリーンショット 2018-12-03 17.50.39.png" src="https://qiita-image-store.s3.amazonaws.com/0/277406/8c51aa57-7f62-7456-a35a-b2dd101170d4.png">

アプリの追加が完了したらFirebaseコンソールの右側のメニューから「Database」をクリックし、表示された画面で「データベースの作成」をクリックします。
<img width="1210" alt="スクリーンショット 2018-12-03 18.25.28.png" src="https://qiita-image-store.s3.amazonaws.com/0/277406/a0778137-17bf-46b1-85c1-96225347c02a.png">

セキュリティルールを設定するダイアログが表示されます。セキュリティ周りを無視するため「テストモード」を選択して「有効にする」をクリックします。実際のプロダクトでは必ず「ロックモード」を選択しましょう。
<img width="1201" alt="スクリーンショット 2018-12-03 19.33.21.png" src="https://qiita-image-store.s3.amazonaws.com/0/277406/0d02e7fb-78dd-c0ed-747b-dfb902fd2c55.png">

**以上！**

何度も言いますが、今のセキュリティルールでは**あるユーザーが他のユーザーの情報を書き換えたり**、**他のユーザーのチャットを書き換えたり**できてしまうので実運用の時はセキュリティルールをしっかり定めましょう！

# クライアントの実装

## 依存を追加
build.gradleにfirestoreライブラリの依存を追加します。

```gradle

dependencies {
    implementation 'com.google.firebase:firebase-firestore:17.1.3'
}
```

dataBindingとRecyclerViewも使いたいので追加します。

```gradle

apply plugin: 'kotlin-android'

android {
    dataBinding {
        enabled = true
    }
}
dependencies {
    implementation 'androidx.recyclerview:recyclerview:1.0.0'
}
```

## ログイン画面

- ユーザー名を入力してもらう
- 入力されたユーザー名が未登録の場合はパスワード作成を促し、入力が終わったら次の画面へ遷移する。
- 入力されたユーザー名が既に登録済みの場合はパスワード入力を促し、登録済みのパスワードと比較し一致したら次の画面へ遷移する。一致しない場合は遷移せずToastを表示する。

を実装します。

以下、ログイン画面のソースコードです。

```Kotlin
class LoginActivity : AppCompatActivity() {

    var users: List<User>? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val binding: ActivityLoginBinding =
                DataBindingUtil.setContentView(this, R.layout.activity_login)

        val db = FirebaseFirestore.getInstance()

        db.collection("users")
                .addSnapshotListener { querySnapshot, _ ->
                    users = querySnapshot?.toObjects(User::class.java)
                }

        binding.login.setOnClickListener {
            val userName = binding.userName.text.toString()
            if (!userName.isNullOrBlank()) {
                val result = users?.find {
                    it.name == userName
                }
                if (result == null) {
                    val layout = layoutInflater.inflate(R.layout.dialog_password, null, false)
                    AlertDialog.Builder(this)
                            .setTitle("パスワードを作成")
                            .setView(layout)
                            .setPositiveButton(android.R.string.ok) { _, _ ->
                                val password = layout.password.text.toString()
                                val user = User(userName, password)
                                db.collection("users")
                                        .add(user)
                                RoomActivity.start(this, userName)
                            }
                            .setNegativeButton(android.R.string.cancel, null)
                            .show()
                } else {
                    val layout = layoutInflater.inflate(R.layout.dialog_password, null, false)
                    AlertDialog.Builder(this)
                            .setTitle("パスワードを入力")
                            .setView(layout)
                            .setPositiveButton(android.R.string.ok) { _, _ ->
                                val password = layout.password.text.toString()
                                if (password == result.password) {
                                    RoomActivity.start(this, userName)
                                } else {
                                    Toast.makeText(this, "パスワードが違います", Toast.LENGTH_SHORT).show()
                                }
                            }
                            .setNegativeButton(android.R.string.cancel, null)
                            .show()
                }
            }
        }
    }
}
```

登録されたユーザーを管理するcollectionを作成し、"users"と名付けます。同時にユーザーの追加を監視します。

```Kotlin
val db = FirebaseFirestore.getInstance()

// Firestore上の"users"collectionへの参照を取得する。
// "users"collectionが無ければ自動で作られる。
db.collection("users")
// "users"コレクションを監視する。変化があればメンバーのusersを更新する。
        .addSnapshotListener { querySnapshot, _ ->
            users = querySnapshot?.toObjects(User::class.java)
        }

// デフォルト引数に空文字を指定しています。引数なしのコンストラクタを用意するためです。
// 引数なしのコンストラクタがないとExceptionが発生するので注意してください。
data class User(var name: String = "",
                var password: String = "")
```

次にログイン処理です。"users"collectionにユーザーが入力した名前が無ければ、新たに追加します。

```Kotlin
val user = User(userName, password)
db.collection("users")
    .add(user)
```

この処理を実行すると、Firebaseコンソール上の"users"collectionに新たにdocumentが追加されることが確認できます。さらにそのdocumentにはstring型の"name"フィールドとstring型の"password"フィールドがあり、それぞれにユーザーが入力した文字列が格納されていることが確認できます。
このようにdocumentと呼ばれる"フィールド"と"collection"を持ったファイルと、そのdocumentの集合であるcollectionによってデータを管理するのがFirestoreです。

これで、Firestore上にユーザーを追加していく準備が整いました。

次回は部屋一覧の取得/監視および、部屋の作成を実装します。