はじめに
こんにちは、とろろです。ととろじゃないです。
Life is Tech!でメンターをしており、普段は iPhoneプログラミングコース と Webサービスコース を担当しています。
最近は学生ベンチャーの企業でエンジニアもしていて、 そこで通知付きトーク機能 のついたiOSアプリを(超短期間で)作ってほしいと言われたので、爆速で開発するために Flutter × Firestore × FCM をフル活用した経験があるのですが、今回のAdvent Calenderではその技術の用法についてまとめていこうと思います。
なお、本記事の解説の中心はトーク通知機能の実装であり、チャット機能のDB構成はコーディングの方法に依るため今回は簡易的な設計となっております。
開発環境(使用ライブラリ) / DB構成
- Flutter
- Firebase(Firestore)
- FCM(Firebase cloud messaging)
Firebase導入やFlutterのセットアップの方法は省略しますので、次のページや記事を参考にしてみてください。
通知機能の実装
次に、通知機能の実装で必要となる部分のコーディングをしていきます。
細かいデータベースの組み方は各々のプロジェクトによって変わるため、その都度変更して頂けたらと思います。
① FCMにおけるトークンの取得
FCMにおけるトークンとは、Firebase Cloud Messagingを利用する際に、各固有デバイスに付与される値のことです。
(公式の参考ドキュメントはこちらから)
このトークンを取得するメソッドと、usersに保存する用のメソッドをそれぞれ作成します。
今回はそれ専用のクラスを作成して、staticメソッドとして定義していきます。
static Future<String> getToken() async {
String token = "-1";
await FirebaseMessaging.instance.getToken().then((doc) {
token = doc!;
});
return token; // 取得したtokenを返し値にする
}
static Future<void> saveToken({required String uid, required String token}) async {
await FirebaseFirestore.instance
.collection("users")
.doc(uid)
.update({
"user_token": token
});
}
② iOS通知のための設定
次の参考ページにしたがって、iOS側の設定を済ませます。
基本的にはFCMの推奨通りに、 p12 ではなく p8 で作成するようにしましょう。
Notificationの設定をしたら、①と同じクラスに通知許可を出すメソッドを定義していきます。
static Future<void> requestPermission() async {
await FirebaseMessaging.instance.requestPermission(
alert: true, // デバイス上で通知を表示するかを決めるプロパティ
announcement: true, // AirPodsに接続時にSiriが通知内容を読み上げるかを決めるプロパティ
badge: true, // ホーム画面のアイコン横に赤いバッジを表示させるかを決めるプロパティ
carPlay: true, // carPlayに接続時に通知を表示するかを決めるプロパティ
sound: true, // 通知が届いた時にサウンドを鳴らすかを決めるプロパティ
);
}
他にもiOS12以降のデバイス向けにprovisional
プロパティも用意されています。詳しくはこちらを参照にしてみてください。ここまで完成したら、アプリ自体に通知が飛ぶための機能は完成しています。
main.dart
で上で定義したメソッドを呼び出し、Firebase Messagingで通知テストを飛ばしてみましょう。
※シミュレーターでは通知が飛ばないので実機で試してください。
③ firebase functionsでFirestoreが更新された時に通知を飛ばす
次に、ターミナル上でプロジェクトファイルに移動し、firebase functionsの設定をしていきます。
基本的には初期値で大丈夫ですが、次の設定項目だけ切り替えておきましょう。
firebase init
¦ こちらにチェックを入れる
(*) Functions: Configure and deploy Cloud Functions
¦
¦ どちらでも大丈夫ですが、今回は TypeScript を用いて開発します。
? What language would you like to use to write Cloud Functions?
JavaScript
> TypeScript
¦
¦ コーディングの方法を統一するESLintを導入するかの質問です。どちらでも良いですが、今回はnにします。
? Do you want to use ESLint to catch probable bugs and enforce style? (Y/n)
n
¦
設定が終わると、プロジェクト直下にfunctionsディレクトリ
が作成されます。
その中にあるsrc/index.ts
に通知を飛ばすための設定を書いていきます。
import * as functions from "firebase-functions";
import * as admin from "firebase-admin";
admin.initializeApp();
const db = admin.firestore();
// documentの引数はfirestoreのDBの作り方に書き換えてください。
// 今回はcontext サブコレクションの中で新たにドキュメントが作成された時をイベントハンドラの対象としています。
exports.messagePushNotify = functions.firestore.document(chats/{roomId}/context/{contextId})
.onCreate(async (snap, context) => {
const roomId = context.params.roomId
const contextId = context.params.contextId
// snapshotから{contextId}ドキュメントの中身を取得します。
const title = snap.data().name
const message = snap.data().context
const contactUserUid = snap.data().contact_uid
// ペイロードの設定をします。
// titleは通知のタイトル、bodyは通知の本文を表します。
const payload = {
notification: {
title: title,
body: messsage,
sound: "default",
}
}
// usersコレクションに保存しているtokenを取得します。
// userのtokenをどこに保存するかに依るので、その都度書き換えてください。
const userRef = db.collection("users").doc(contact_user_uid);
const userDoc = await userRef.get();
try {
const token = userDoc.get("user_token")
// このmessagingメソッドで通知を飛ばします。
admin.messaging().sendToDevice(token, payload);
} catch (error) {
console.log(`Error!: ${error}`);
}
})
これで完成です!あとは、firestoreの指定部分が作られる度に通知がデバイスに飛ぶようになります。
まとめ
FCMを使えばフロントドリブンな通知処理を簡単に実行できちゃう嬉しさがあります!
通知がどのような処理によって行われているのかを知らなくともApple大先生とGoogle大先生のNotification, Firebase周りのパッケージでサクッと作れるため、短期間・少人数で機能が完成できるのがとても助かりました。
より便利なFirebase周りのパッケージや機能を、また探して遊んでみようかな〜〜〜と思います。
明日はベテラン Life is Techer!であるりょうさんの記事になります!
それでは、引き続き東海 Life is Tech! Advent Calenderをお楽しみください!