The Round

合同会社ナイツオの開発ブログ

[PR] 5分から相談できるGCP™ 開発コンサル!→こちら

Firebaseでチャットアプリを作る日記(9日目)〜 メッセージの自動更新

メッセージの自動更新

昨日の記事 で、メッセージ一覧の取得を行いました。

一人で書き込んでる分には一見追加したメッセージが一覧に反映されていきますが、これだけでは他のユーザーが登録したメッセージが反映されません。

幸いCloud Firestore in Native modeにはリアルタイムにデータの更新を受け取る機能がありますので、これを使って修正します。

Get realtime updates with Cloud Firestore  |  Firebase

コード

参考: https://firebase.google.com/docs/firestore/query-data/listen#listen_to_multiple_documents_in_a_collection

getMessages関数を下記の様に修正します。

export function getMessages(roomId, email) {
  // realtime updateする様にしたのでクエリ発行済のルームは再取得しません
  if (hasMessagesOf(roomId)) {
    return get(messagesOf(roomId));
  }

  let db = firebase.firestore();

  return db.collection("rooms").doc(roomId).collection("messages").where("members", "array-contains", email).orderBy("createdAt")
    .onSnapshot(function(querySnapshot) {
        let _messages = [];
        querySnapshot.forEach(function(msgRef) {
          let msg = msgRef.data({serverTimestamps: "estimate"});
          msg.id = msgRef.id;
          _messages = [..._messages, msg];
        });

        // UIに反映(Svelte)
        messagesOf(roomId).set(_messages);

        return _messages;
    }, function(error) {
        console.log("Error getting messages: ", error);
    });
}

[QueryのonSnapshot関数]を使用することで、メッセージが追加されたら都度処理が実行されるようになります。

自分が追加した場合も同じく呼び出されるので、メッセージ追加処理で行なっていたUIへの反映処理(Svelte)は削除しました。

    // 登録したmessageをUIに追加(Svelte)
    // realtime update litenerで更新する為コメントアウト
    //messagesOf(room.id).update(list => list.concat(added));

タイムスタンプで少しハマった

メッセージ追加処理 で、メッセージにサーバー側タイムスタンプを保存する為 firebase.firestore.FieldValue.serverTimestamp() を設定していましたが、これを利用すると登録直後の更新通知スナップショットにはタイムスタンプがnullで設定されていました。わずかな時間をおいてタイムスタンプが設定されたスナップショットが入ってきましたが、少しタイムラグがある様です。

DocumentSnapshotのdataメソッドSnapshotOptionsを渡すと挙動を変えることができます。

serverTimestamps: "estimate" | "previous" | "none"
説明
estimate 概算時刻が設定される。最終的に設定される時刻とはズレる
previous 更新前の値が設定される
none (デフォルト)nullが設定される

リスナーのデタッチ

本アプリでは一度取得したルームのメッセージをチェックし続けるので、リアルタイム更新リスナーのでタッチ(解除)は行なっていません。

データが不要になったら適切にデタッチしてあげないと、使用リソースが無駄に増え続けてしまいます。

参考 : Detach a listener

var unsubscribe = db.collection("cities")
    .onSnapshot(function (){
      // Respond to data
      // ...
    });

// Later ...

// Stop listening to changes
unsubscribe();

まとめ

これでようやくチャットアプリっぽくなってきました。

f:id:knightso:20191221124555p:plain
いま画面はこんなカンジ

リアルタイム更新リスナーは、Cloud DatastoreやCloud Firestore(Datastore mode)にはない機能です。 すごく便利ですね!!

便利すぎて怖くなっているのですが、どの程度気軽に使ってよいものなんでしょうかね(^_^;;

次は検索機能にトライします!

(明日は更新できないかも 🙇)