Firebaseの公式チュートリアルをやっていく(後編)

2019年7月28日

前回の内容

通知機能の実装

Firebase Cloud Messaging (FCM)というのを使って通知機能を実装する。
public/manifest.jsonを開く
以下を変更せずにそのままコピペで貼り付け。
gcm_sender_idを記述していなかったので、ちょっとハマった。

{
  "name": "Friendly Chat",
  "short_name": "Friendly Chat",
  "start_url": "/index.html",
  "display": "standalone",
  "orientation": "portrait",
  "gcm_sender_id": "103953800507"
}

FCM service workerを追加する

メッセージの通知と表示をするために、アプリにService Workerを実装する。
public配下に firebase-messaging-sw.js を作成する。
以下のように編集する。
firebase/6.04/firebase….
の6.04部分。これたぶん実際にチュートリアルを実施するタイミングで変わるはず。
気になる場合は、githubでダウンロードしたソースのバージョンに合わせて変更すればよい。
public/index.htmlの一番下のほうにCDNでfirebaseを読み込むところがあるので、そこのバージョンに合わせればよいと思います。

importScripts('/__/firebase/6.0.4/firebase-app.js');
importScripts('/__/firebase/6.0.4/firebase-messaging.js');
importScripts('/__/firebase/init.js');

firebase.messaging();

FCM device tokensを取得する

ユーザがサインインしたタイミングでsaveMessagingDeviceTokenメソッドによりFCM device tokensをfirestoreに保存するようにする。 FCM device tokens は通知先を識別するために使用する。
public/scripts/main.js を編集する。

// Saves the messaging device token to the datastore.
function saveMessagingDeviceToken() {
  firebase.messaging().getToken().then(function(currentToken) {
    if (currentToken) {
      console.log('Got FCM device token:', currentToken);
      // Saving the Device Token to the datastore.
      firebase.firestore().collection('fcmTokens').doc(currentToken)
          .set({uid: firebase.auth().currentUser.uid});
    } else {
      // Need to request permissions to show notifications.
      requestNotificationsPermissions();
    }
  }).catch(function(error){
    console.error('Unable to get messaging token.', error);
  });
}

この実装だけでは、まだ通知は機能していない。
ユーザが通知機能を許可しないといけない。

パーミッションリクエストのダイアログを表示させる

ユーザが通知機能の許可を与えるために、パーミッションリクエストのダイアログを表示させる。
public/scripts/main.js を編集する。

// Requests permission to show notifications.
function requestNotificationsPermissions() {
  console.log('Requesting notifications permission...');
  firebase.messaging().requestPermission().then(function() {
    // Notification permission granted.
    saveMessagingDeviceToken();
  }).catch(function(error) {
    console.error('Unable to get permission to notify.', error);
  });
}

ダイアログがでる

ブラウザの開発者モードのコンソールでFCM device tokenを取得しておく。
通知機能の動作確認で必要なので。

通知機能の動作確認

動作確認のために、FirebaseのServer Keyが必要となる。
Firebaseコンソール→プロジェクトの設定→クラウドメッセージング
にアクセスして Server Keyを取得する。

YOUR_SERVER_KEYとYOUR_DEVICE_TOKENにそれぞれ、サーバーキーと FCM device token を入力してcurlで投げる。

curl -H "Content-Type: application/json" \
     -H "Authorization: key=YOUR_SERVER_KEY" \
     -d '{
           "notification": {
             "title": "New chat message!",
             "body": "There is a new message in FriendlyChat",
             "icon": "/images/profile_placeholder.png",
             "click_action": "http://localhost:5000"
           },
           "to": "YOUR_DEVICE_TOKEN"
         }' \
     https://fcm.googleapis.com/fcm/send

curlしてみたけど、うまくいかんよ?
カフェでやってるからか?

<HTML>
<HEAD>
<TITLE>The request was missing an Authentication Key (FCM Token). Please, refer to section "Authentication" of the FCM documentation, at https://firebase.google.com/docs/cloud-messaging/server.</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000">
<H1>The request was missing an Authentication Key (FCM Token). Please, refer to section "Authentication" of the FCM documentation, at https://firebase.google.com/docs/cloud-messaging/server.</H1>
<H2>Error 401</H2>
</BODY>
</HTML>

どうやら gcm_sender_id がちゃんと記述されていなかったため、通知が出なかったようだ。
再度、ちゃんと gcm_sender_id を記述してcurlすると以下のように、ちゃんと通知が出るようになった。

Cloud Firestoreセキュリティルール

Cloud Firestoreは、独自にルール記述用の言語がある。
これにより、アクセス権限,セキュリティ,バリデーション等の設定をする。
Firebaseプロジェクトを設定した際、セキュリティルールをテストモードとして登録した。このため、データベースへのアクセス制限がされていない。
FirebaseのDatabase→ルールで現在の設定がどうなっているかわかる。

また、直接ここで編集することで、設定をいじることができる。
以下のように設定する。
編集が終わったら公開ボタンを押す。

service cloud.firestore {
  match /databases/{database}/documents {
    // Messages:
    //   - Anyone can read.
    //   - Authenticated users can add and edit messages.
    //   - Validation: Check name is same as auth token and text length below 300 char or that imageUrl is a URL.
    //   - Deletes are not allowed.
    match /messages/{messageId} {
      allow read;
      allow create, update: if request.auth != null
                    && request.resource.data.name == request.auth.token.name
                    && (request.resource.data.text is string
                      && request.resource.data.text.size() <= 300
                      || request.resource.data.imageUrl is string
                      && request.resource.data.imageUrl.matches('https?://.*'));
      allow delete: if false;
    }
    // FCM Tokens:
    //   - Anyone can write their token.
    //   - Reading list of tokens is not allowed.
    match /fcmTokens/{token} {
      allow read: if false;
      allow write;
    }
  }
}

記述方法については以下のドキュメントも参照
https://firebase.google.com/docs/firestore/security/get-started

もう一つの変更方法として、ローカルファイルをいじってルールを変更する方法
publish/firebase.jsonを編集する

{
//ここを追加
  "firestore": {
    "rules": "firestore.rules"
  },
  "hosting": {
    "public": "./public",
    "headers": [{
      "source" : "**/*.@(js|html)",
      "headers" : [ {
        "key" : "Cache-Control",
        "value" : "max-age=0"
      } ]
    }]
  }
}

先ほどのルールをweb-start/firestore.rulesとして配置する。
以下をコマンドラインで実行

firebase deploy --only firestore

以下のように出力されれば成功
i  deploying firestore
i  firestore: checking firestore.rules for compilation errors...
✔  firestore: rules file firestore.rules compiled successfully
i  firestore: uploading rules firestore.rules...
✔  firestore: released rules firestore.rules to cloud.firestore

Cloud Storage セキュリティルール

コンソールのStorage→ルールから変更できる。

以下のルールを定義する
・それぞれのユーザは、自分のフォルダのみ書込許可とする
・誰でもcloud strageから読込許可
・アップロードファイルはイメージに限る
・アップロードの容量制限として5MB以下とする

Cloud Firestoreのルールと同様で、こちらも二種類の方法で設定を適用することができる。
今回は、ローカルファイルをデプロイする方法のみ記録しておく。

web-start/storage.rules を作成し、以下のように編集する。

// Returns true if the uploaded file is an image and its size is below the given number of MB.
function isImageBelowMaxSize(maxSizeMB) {
  return request.resource.size < maxSizeMB * 1024 * 1024
      && request.resource.contentType.matches('image/.*');
}

service firebase.storage {
  match /b/{bucket}/o {
    match /{userId}/{messageId}/{fileName} {
      allow write: if request.auth != null && request.auth.uid == userId && isImageBelowMaxSize(5);
      allow read;
    }
  }
}

web-start/ firebase.json を以下のように編集する。

{
  "firestore": {
    "rules": "firestore.rules"
  },
//ここを追加
  "storage": {
    "rules": "storage.rules"
  },
  "hosting": {
    "public": "./public",
    "headers": [{
      "source" : "**/*.@(js|html)",
      "headers" : [ {
        "key" : "Cache-Control",
        "value" : "max-age=0"
      } ]
    }]
  }
}

Firebase CLIからStrageルールをデプロイすることで設定を反映させる。

firebase deploy --only storage

コマンドを実行して、以下のような結果なら成功

念のため、Firebaseのコンソールでも変更されているか確認。
ローカルファイルで定義したルールと同じになっていることが確認できる。

パフォーマンスデータを収集する

Firebase パフォーマンスモニタリングをwebアプリに統合する方法はいくつか
存在する。
チュートリアルでは、 Hosting URL から アプリに統合する方法となっている。
以下のURLはFirebaseパフォーマンスモニタリングのドキュメントです。

https://firebase.google.com/docs/perf-mon/get-started-web

既に firebase-performance.js と init.jsがweb-start/public/index.htmlに定義
されているので、ユーザがサイトを訪れるたびに、自動的にページロード情報,
ネットワークリクエスト情報を収集されるようになっている。

first input delay(FID)を測定する

※以下はより詳細なパフォーマンスを測定するためのステップです。
よって、FIDを測定してもしなくてもどちらでもよいです。

FIDとは、ユーザの操作(ボタンクリック,リンクのクリック等)を起点として
ブラウザが応答を返すまでの時間を指標としたものです。
要は、サイトがサクサク動いているか把握するための指標だと思えばよいです。
より詳しくFIDについて確認したい場合は以下のリンクを参照。

https://developers.google.com/web/updates/2018/05/first-input-delay

FIDを測定するために、 web-start/public/index.html でコメントアウトされて
いる以下の部分をコメント解除してください。

<!-- TODO: Enable First Input Delay polyfill library. -->
<script type="text/javascript">!function(n,e){var t,o,i,c=[],f={passive:!0,capture:!0},r=new Date,a="pointerup",u="pointercancel";function p(n,c){t||(t=c,o=n,i=new Date,w(e),s())}function s(){o>=0&&o<i-r&&(c.forEach(function(n){n(o,t)}),c=[])}function l(t){if(t.cancelable){var o=(t.timeStamp>1e12?new Date:performance.now())-t.timeStamp;"pointerdown"==t.type?function(t,o){function i(){p(t,o),r()}function c(){r()}function r(){e(a,i,f),e(u,c,f)}n(a,i,f),n(u,c,f)}(o,t):p(o,t)}}function w(n){["click","mousedown","keydown","touchstart","pointerdown"].forEach(function(e){n(e,l,f)})}w(n),self.perfMetrics=self.perfMetrics||{},self.perfMetrics.onFirstInputDelay=function(n){c.push(n),s()}}(addEventListener,removeEventListener);</script>

Firebase Hostingを使ってアプリをデプロイする

コマンドラインでweb-startまで移動して以下を実行

firebase deploy --except functions

以下のコマンドでホスティングしているサイトを開いてくれる。

 firebase open hosting:site 

ホスティングに関しての公式ドキュメントは以下

https://firebase.google.com/docs/hosting/#how_does_it_work

Firebase

Posted by takumioda