FirebaseでWebチャットアプリをデプロイするまで（1時間コース）

さて先日の13日、[Google for Mobile 2016](https://events.withgoogle.com/google-for-mobile/program/)が横浜で開催されましたね。
びっくりしたのは、15時まで毎時間[Firebase](https://firebase.google.com/)の話が入っていて、GoogleさんのFireabaseへの力の入れようがハンパない感じです！

![GoogleforMobile2016](https://lh3.googleusercontent.com/x35DlR4339Z3VQEi4bjTr6o8nO1UA1Yl8Ehce6pEJdvwMehQcM3kdsyEcVEB78wy4puIQKrsrDqCXcimC8i1QA=w2000)


__基調講演__

| 時間 | 内容 | 登壇者 |
|:-----------|:------------|:------------|
| 9:30-10:50      |        Firebase の概要【同時通訳】|     Laurence Moroney      |

__ブレイクアウトセッション__

| 時間 | 内容 | 登壇者 |
|:-----------|:------------|:------------|
|-11:30    |Firebase Analytics の使い方【同時通訳】|Fontaine Foxworth      |
|-12:00    |Firebase データベース、ストレージ、ホスティングの使い方【同時通訳】|Jacob Wenger / Mike Mcdonald  |
|-12:30    |Firebase を使った高品質アプリの作り方　【同時通訳】|Doug Stevenson      |
|-13:00    |Firebase を使ったアプリへの連携ログイン実装方法　【同時通訳】|Laurence Moroney     |
|-13:30    |Firebase を使った通知　【同時通訳】|Thomas Bouldin    |
|-14:00    |Firebase で設定するアプリインデックス（App Indexing）　【同時通訳】|Laurence Moroney    |
|-14:30   |Firebase ケーススタディ|小川 健太郎 様、今井 猛 様、山下 大介 様    |
|-15:00    |Firebase Analytics を使ったアプリ プロモーションの可能性|横山 明子 様、菊地 慧 様 、福西 祐樹 様    |

これからさらにFirebase盛り上がりそうですね。

でFirebaseがどんなものか。
Webやアプリのサービスの開発に関わるほとんどのことをサポートしてくれるサービスです。
一言では言えないくらい機能が豊富で、AnalyticsもPushもリアルタイムDBもストレージもホスティングもデバイステストもA/Bテストも。。。もうなんでもできる感じです。しかもほぼタダで。

実はGoogleさんが１時間でできるサンプルアプリのチュートリアルを用意してくれてます。
これを触れば、リアルタイムDBやストレージ、ホスティングに関して、Firebaseの事がよくわかると思います。
[Build a Real Time Web Chat App (英語)](https://codelabs.developers.google.com/codelabs/firebase-web/index.html)

作ってみたところ、ざっと２時間くらいかかりました。^^;
今回そのエッセンスだけ取り出して、このQiitaページに書いています。
なので、予定通り1時間でできると思います。

では始めていきます。  
## FirebaseでWebチャットアプリをデプロイするまで（1時間コース）

↓作るものはこういうのです
![画像](https://codelabs.developers.google.com/codelabs/firebase-web/img/eb22bdc11ce003e0.png)  


### 1 概要 （残り60分）
Firebaseを使った簡単なWebアプリの実装と、Firebaseを使ったチャットクライアントのデプロイ

#### 何が学べるか？
・Firebaseのリアルタイムデータベースとストレージの使い方
・Firebaseの認証
・FirebaseにホストしたWebアプリの公開

#### 何が必要か？
・テキストエディタ [WebStorm](https://www.jetbrains.com/webstorm),、[Atom](https://atom.io/)、[Sublime](https://www.sublimetext.com/)とか。
・サンプルコード（2でやるやつ）
・[Chromeブラウザ](https://www.google.co.jp/chrome/browser/desktop/)

### 2 サンプルコードの取得（残り59分）
[Githubのサンプルコードページ](https://github.com/firebase/friendlychat)からクローンする
コマンドの場合は`git clone https://github.com/firebase/friendlychat`

### 3 アプリのインポート（残り57分）
クローンした中の`web-start`ディレクトリを使っていきます。  

### 4 Firebaseプロジェクトの作成と、アプリの設定（残り56分）
#### プロジェクトの作成
[Firebaseコンソール](https://console.firebase.google.com/)で「新規プロジェクト作成」ボタンをクリック  
#### アプリの認証
「WebアプリにFirebaseを追加」を選択
すると以下のようなjavascriptのスニペットを得るので、
「コピー」ボタンを押して、`index.html`の`TODO`のところに貼り付ける
![画像](https://codelabs.developers.google.com/codelabs/firebase-web/img/2d1763dad02edba6.png)

ここで稀に`storageBucket`の値が`""`となっている場合があるので、その際には一旦窓を閉じて、再度「WebアプリにFirebaseを追加」を選択する

#### Google認証を有効化
Firebaseのコンソールで
Auth > 「ログイン方法を設定」＞Goole ＞「有効にする」をスライドする＞「保存」  
をクリック

### 5 Firebaseコマンドラインのインストール（残り51分）
Firebase Command Line Interface (CLI) はローカルで作っているアプリをFirebaseのホスティングサービスにUpするために必要
まずFirebaseのコンソールで`Hosting`を選択
「スタートガイド」ボタンをクリック
あとは画面の通りに進めるだけ。  

CLIには[npm](https://www.npmjs.com/)と[node.js](https://nodejs.org/en/)が必要なのでなければインストールする
もしインストールに失敗したら、[npmのパーミション設定を変更する](https://docs.npmjs.com/getting-started/fixing-npm-permissions)必要がある。
インストール終わったらコマンドで以下を叩いて

```
firebase version
```
`3.*.*`とか出ればOK。もし`2.*.*`とか出たらもう古い。  
firebaseにログインする。コマンドラインで

```
firebase login
```
`cd`で`web-start`ディレクトリに移動して以下のコマンド

```
firebase use --add
```
ここで出てきたProjectIdを選択する  

### 6 アプリの起動（残り49分）
`web-start`フォルダで以下のコマンドを実行

```
firebase serve
```
h ttp://localhost:5000/なんちゃらかんちゃら…ってでてきたら、ブラウザで
[http://localhost:5000]( http://localhost:5000)を叩けばOK!
サンプルアプリが見れるはず
ただこのままじゃ何もできないので修正

### 7 ユーザーのサインイン機能（残り48分）
#### Firebaseの認証の初期化

`scripts/main.js`の`FriendlyChat.prototype.initFirebase`関数のところを修正する必要がある   
 
```
FriendlyChat.prototype.initFirebase = function() {
  // Shortcuts to Firebase SDK features.
  this.auth = firebase.auth();
  this.database = firebase.database();
  this.storage = firebase.storage();
  // Initiates Firebase auth and listen to auth state changes.
  this.auth.onAuthStateChanged(this.onAuthStateChanged.bind(this));
};
```

#### GoogleによるFirebaseの認証
main.jsの`scripts/main.js`の`FriendlyChat.prototype.signIn`関数のとこも   
 
```
// Signs-in Friendly Chat.
FriendlyChat.prototype.signIn = function() {
  // Sign in Firebase using popup auth and Google as the identity provider.
  var provider = new firebase.auth.GoogleAuthProvider();
  this.auth.signInWithPopup(provider);
};
```
`FriendlyChat.prototype.signOut`も  

```
	// Sign out of Firebase.
	this.auth.signOut();
```
`FriendlyChat.prototype.onAuthStateChanged`も  

```
	// Get profile pic and user's name from the Firebase user object.
	var profilePicUrl = user.photoURL; // Only change these two lines!
	var userName = user.displayName;   // Only change these two lines!
```
`FriendlyChat.prototype.checkSignedInWithMessage`も  

```
  // Return true if the user is signed in Firebase
  if (this.auth.currentUser) {
    return true;
  }
```

ここまで書いたら、ブラウザをリロードするか、コマンドで`firebase serve`を打つ  
ブラウザで`localhost:5000`を見たら、右上にGoogleによるログイン機能が実装されてるはず  

### 8 メッセージを読む（残り38分）
FirebaseコンソロールのDatabaseセクションをクリック  
「・・・」（←・は縦に並んでる）ボタンの`JSONをインポート`ボタンをクリック  
ローカルの`firebase_sample`ディレクトリにある`initial_messages.json`をインポートすればOK  

#### メッセージを同期

main.jsの`FriendlyChat.prototype.loadMessages`編集  

```
FriendlyChat.prototype.loadMessages = function() {
  // Reference to the /messages/ database path.
  this.messagesRef = this.database.ref('messages');
  // Make sure we remove all previous listeners.
  this.messagesRef.off();

  // Loads the last 12 messages and listen for new ones.
  var setMessage = function(data) {
    var val = data.val();
    this.displayMessage(data.key, val.name, val.text, val.photoUrl, val.imageUrl);
  }.bind(this);
  this.messagesRef.limitToLast(12).on('child_added', setMessage);
  this.messagesRef.limitToLast(12).on('child_changed', setMessage);
};
```
ここまで書いたら、ブラウザをリロードするか、コマンドで`firebase serve`を打つ  
ブラウザで`localhost:5000`を見たら、右上にGoogleによるログイン機能が実装されてるはず  

### 9 データベースセキュリティルール（残り28分）
#### セキュリティールールの設定
バリデーションの設定とかできる。nullチェックとか。  
やり方は、Firebaseコンソール画面のDatabeaseセクションのルールタブで。  
デフォルトのルールはこんな感じ  

```
{
  "rules": {
    ".read": "auth != null",
    ".write": "auth != null"
  }
}
```
９項はオプションなので以下省略  

### 10 メッセージの送信（残り23分）
#### 実装
main.jsの`FriendlyChat.prototype.saveMessage`  

```
// Saves a new message on the Firebase DB.
FriendlyChat.prototype.saveMessage = function(e) {
  e.preventDefault();
  // Check that the user entered a message and is signed in.
  if (this.messageInput.value && this.checkSignedInWithMessage()) {
    var currentUser = this.auth.currentUser;
    // Add a new message entry to the Firebase Database.
    this.messagesRef.push({
      name: currentUser.displayName,
      text: this.messageInput.value,
      photoUrl: currentUser.photoURL || '/images/profile_placeholder.png'
    }).then(function() {
      // Clear message text field and SEND button state.
      FriendlyChat.resetMaterialTextfield(this.messageInput);
      this.toggleButton();
    }.bind(this)).catch(function(error) {
      console.error('Error writing new message to Firebase Database', error);
    });
  }
};
```
ここまで書いたら、ブラウザをリロードするか、コマンドで`firebase serve`を打つ  
ブラウザで`localhost:5000`を見たら、送信が実装されてるはず  

### 11 画像送信（残り18分）
#### Firebaseのストレージに画像を保存
main.jsの`FriendlyChat.prototype.saveImageMessage`  

```
// Saves the a new message containing an image URI in Firebase.
// This first saves the image in Firebase storage.
FriendlyChat.prototype.saveImageMessage = function(event) {

  ...

  // Check if the user is signed-in
  if (this.checkSignedInWithMessage()) {

    // We add a message with a loading icon that will get updated with the shared image.
    var currentUser = this.auth.currentUser;
    this.messagesRef.push({
      name: currentUser.displayName,
      imageUrl: FriendlyChat.LOADING_IMAGE_URL,
      photoUrl: currentUser.photoURL || '/images/profile_placeholder.png'
    }).then(function(data) {

      // Upload the image to Firebase Storage.
      var uploadTask = this.storage.ref(currentUser.uid + '/' + Date.now() + '/' + file.name)
          .put(file, {'contentType': file.type});
      // Listen for upload completion.
      uploadTask.on('state_changed', null, function(error) {
        console.error('There was an error uploading a file to Firebase Storage:', error);
      }, function() {

        // Get the file's Storage URI and update the chat message placeholder.
        var filePath = uploadTask.snapshot.metadata.fullPath;
        data.update({imageUrl: this.storage.ref(filePath).toString()});
      }.bind(this));
    }.bind(this));
  }
};
```
#### 画像をFirebaseのストレージから表示する
main.jsの`FriendlyChat.prototype.setImageUrl`  

```
// Sets the URL of the given img element with the URL of the image stored in Firebase Storage.
FriendlyChat.prototype.setImageUrl = function(imageUri, imgElement) {
  // If the image is a Firebase Storage URI we fetch the URL.
  if (imageUri.startsWith('gs://')) {
    imgElement.src = FriendlyChat.LOADING_IMAGE_URL; // Display a loading image first.
    this.storage.refFromURL(imageUri).getMetadata().then(function(metadata) {
      imgElement.src = metadata.downloadURLs[0];
    });
  } else {
    imgElement.src = imageUri;
  }
};
```
ここまで書いたら、ブラウザをリロードするか、コマンドで`firebase serve`を打つ  
ブラウザで`localhost:5000`を見たら、画像送信が実装されてるはず  

### 12 ストレージセキュリティルール（残り8分）
これも9と同じでバリデーションとか設定できるよう。オプションなので今回は省略  

### 13 Firebaseのホスティングにアプリをデプロイする（残り3分）

firebase.jsonを以下に書き換え  

```
{
  // 9のステップを踏んだ場合は以下のコメントを外す
  // "database": {
  //   "rules": "database-rules.json"
  // },
  // 12のステップを踏んだ場合は以下のコメントを外す
  // "storage": {
  //   "rules": "storage.rules"
  // },
  "hosting": {
    "public": "./",
    "ignore": [
      "firebase.json"//,
      // "database-rules.json",//9のステップを踏んだ場合はコメントを外す（上の行のコメントも）
      // "storage.rules"// 12のステップを踏んだ場合はコメントを外す（同上）
    ]
  }
}
```
カレントディレクトリの名前が`public`になってるので、`web-start`ディレクトリをコピペして、そのディレクリ名を`public`に変更。  
変更したら、コマンドラインで`public`ディレクトリへ`cd`  
あとは以下のコマンドを打てばデプロイ完了  

```
firebase deploy
```
これでWebに公開されました！
簡単なリアルタイムチャットはこれで作れますね。
以上です。  

てか、javascriptだけでWebサービス作るなら、もうレンタルサーバもAWSもHerokuもいらんよって話になっちゃってますね。コレ。。
Firebaseすごいな〜
またFirebaseネタアップしますね。