Cloud FunctionsでFireStoreにデータが保存をトリガーに通知を送信する

#はじめに
`Cloud Messaging`で通知を送信できたはいいけど、LINEみたいにメッセージを受信した時に通知を送信するのはどうするのか気になったので調べました。
ちなみにめっちゃたくさんの記事を読み漁りました。
公式のドキュメントも読んだけど、日本語がむずがしい！もっと簡単な日本語にしてくれ！
今回は`Firestore`にデータが保存されたときに通知を送信するようにしてみます。

本記事は`Cloud Messaging`にてテスト用の通知の送信はできるところまでセットアップしていることを前提に進めていきます。

また、`Cloud Functions`を利用するに当たって料金プランを無料のSparkから従量制のBlazeに変更している必要があります。

#AppDelegateの設定
`Firebase`の公式にも書いてある通り

```swift
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
}
```

この関数でトークンをサーバーに送信します。
関数の呼び出しタイミングは公式に以下ように書いています。
>・登録トークンが新規の場合、そのトークンをアプリケーション サーバーに送信します。
・登録トークンをトピックに登録します。これは新規登録の場合、またはユーザーがアプリを再インストールした場合にのみ必要です。

トークンは通知を送信する際に必要なので

```swift
    func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
        UserDefaults.standard.setValue(fcmToken, forKey: "fcmToken")
        UserDefaults.standard.synchronize()
    }
```
と、こんな感じで一時的に保存しておいてfirestoreにユーザーデータを保存する際にtokenも保存しましょう。
`swift`側のコードはこれだけです。

#Cloud Functions
`Cloud Functinos`の設定をしていきましょう。
※今回はnpmを使うのでnodeとnpmのインストールが必須です。
ターミナルで次のコマンドを順番に入力してください。

```
npm install -g firebase-tools
firebase login
firebase init
```

`firebase init`を入力するとなんか聞かれますが

>- 利用する機能：Functions: Configure and deploy Cloud Functions
- デフォルトのFirebaseプロジェクト：作成したFirebaseプロジェクトを選択します
- 言語：JavaScript
- ESLintを利用するか：N
- 今すぐ依存関係をインストールするか：Y

このように答えてください。
利用する機能はカーソルを合わせたらEnterではなくSpace->Enterの順に入力してください。

これで作業ディレクトリにfuncionsフォルダなどができています。
できているはずです。
できてるよね...?

次項から`JavaScript`を書いていきます。

#Index.js
先程できたfunctinonsディレクトリ内にindex.jsというファイルができていると思います。
それを開くと

```javascript
const functions = require("firebase-functions");
```
というコードとコメントだけが書かれていると思います。
これはプログラミングによくある　``おまじない💕``　です。
まあこれを書くとfunctionsが使えるよというものなので消さないでください。
おまじないをさらに追加します。

```javascript
const functions = require("firebase-functions");
// Admin SDKを使うのに必要（おまじない）
以下を追記
const admin = require("firebase-admin");
admin.initializeApp();
const firestore = admin.firestore();
```

次にプッシュ通知のタイトルやメッセージ内容です。

```javascript
const pushMessage = (fcmToken, userName) => ({
    notification: {
        title: "保存が完了しました",
        body: `${userName}の保存が完了しました`,
    },
    data: {
        hoge: `fuga`,// 任意のデータをおくれる
    },
    token: fcmToken,
});
```

jsでは文字列内に変数をねじ込むにはバッククォートなので気をつけてください。

ここまでは正直前座です。
ここから通知を送信するのに必要なコードになります。

コード全体を載せます。

```javascript
exports.saveUser = functions.firestore
    .document('users/{userID}')
    .onCreate((snapshot, context) => {
        const userID = context.params.userID;
        console.log('ユーザ情報をFirestoreに保存しました. userID: ', userID);
        const userRef = firestore.doc(`users/${userID}`);
        userRef.get().then((user) => {
            const userData = user.data();
            admin.messaging().send(pushMessage(userData.fcmToken, userData.name))
                .then(res=> {
                    console.log('メッセージの送信に成功: ', response);
                })
                .catch(err => {
                    console.log('メッセージの送信に失敗: ', e);
                })
        }).catch(err => {
            console.log("失敗:", err);
        })
    });
```

解説部いきます。
一行目の
`exports.saveUser`ですが`saveUser`がデプロイする関数名です。
`exports.{関数名}`です。

`.document('users/{userID}')`はセキュリティルールと同じくワイルドカードをつかっています。

`onCreate`は`firestore`にデータが保存されたのをトリガーに`{}`内の処理を実行します。

`context.params.userID`はワイルドカードで指定したドキュメントIDを取得します。
`userID`がワイルドカードと同じでないとエラーになり思ったような処理ができません。
私はここでめっちゃ詰まってました。

`userRef.get()`でドキュメントを取得し、`data()`でデータを取得します。
今回の場合

```javascript
then((user) => {
            const userData = user.data();
}
```
この部分です。
`JavaScript`ほぼ触ったことないけど`then`て`cathc`あるし、例外処理？と思いながら書いてました。違ってもボコスカに叩かないでくださいね。
調べていないので。

ここまでで保存された時に何かできる状態になりました。
あとは通知を送信するだけですね。

```javascript
admin.messaging().send(pushMessage(userData.fcmToken, userData.name))
                .then(res=> {
                    console.log('メッセージの送信に成功: ', response);
                })
                .catch(err => {
                    console.log('メッセージの送信に失敗: ', e);
                })
```
これで送信できます。
`send(pushMessage(...))`で通知のメッセージ内容を渡してあげます。

#Deploy


ここまできたら

```
firebase deploy
```
とターミナルに入力してください。

そして`firestore`にデータを保存すると...
きっと通知が来るはずです。

ちなみに

```
firebase deploy --only functinons:{関数名}
```
で任意の関数だけdeployできるそうです。


#最後に
丸一日（他のこともしていたから実際は５時間くらい）かけてハマりにハマってしまって頭を抱えた`cloud functinos`でしたが、時間をかけたおかげで自分の中ではなんとか理解できたと思います。
それをしっかりこの記事でアウトプットできているかは別ですが。

普段SwiftばっかりなのでJavaScriptは難しかったです。
この機会にすこし勉強してみようかな。

質問、ご指摘等ございましたら是非コメントやTwitterのDMにてメッセージをください。