怠慢プログラマーの備忘録

怠慢でナマケモノなプログラマーの備忘録です。

Firestoreの特定のCollectionをCloudFunctionsで監視してPushNotificationを送信する[備忘録]

チャットアプリなどでFirestoreの特定のCollectionが変更された際に対象ユーザーにPushNotificationを送信するといったシーンの実装方法の備忘録です。

Firebase

CloudMessagingがPushNotificationを送信する際に必要になるfcmTokenはFirestoreに保存しておきます。 アプリ側でfcmTokenを取得する際のコードは下記になります。

Sample.swift

InstanceID.instanceID().instanceID { [weak self] (result, error) in
          if let error = error {
            print("Error fetching remote instance ID: \(error)")
          } else if let result = result {
            result.token
          }
}

大まかな流れとしては、

①fcmTokenをアプリ側からFirestoreへ登録する

②CloudFunctionsにFirestoreのCollectionをトリガーとしたfunctionを記載

③function内でPushNotificationの送信先のfcmTokenを特定して送信

CloudFunctionsに記載するfunctionは以下の通りです。

index.js

const functions = require('firebase-functions');
const admin = require('firebase-admin');
const serviceAccount = require('./config.json');
const auth = admin.auth();
const firestore = admin.firestore();
admin.initializeApp({
  credential: admin.credential.cert(serviceAccount),
  databaseURL: "https://{dbURL}.firebaseio.com"
});

/* Observe 'Rooms' and send push notifications when changes occur */ 
exports.sendPushNotification = functions.firestore.document('rooms/{roomId}').onWrite(async (change, context) => {
  try {
    // 最新のTalkを取得
    const newVal = change.after.data();
    const date = newVal.update_date;
    const talksQuery = await firestore.collection('talks').where('date', '==', date).get();
    talksQuery.forEach(async doc => {

      // 対象ユーザーのfcmToken取得
      const userId = doc.get('user_id');
      const infoQuery = await firestore.collection('user_info').where('user_id', '==', userId).get();

      if (infoQuery.size != 0) {
        infoQuery.forEach(doc => {
          const token = doc.get('fcm_token');
  
          // 通知用payload
          const payload = {
              notification: {
                title: "新着メッセージ",
                body: "チャットに新着メッセージがあります",
                badge: "1",
                sound: "default",
              }
          };
          _pushToDevice(token, payload);
        });
      }
    });
  } catch(e) {
    console.error(e)
  }
});

function _pushToDevice(token, payload){
  const options = {
    priority: "high",
  };
  admin.messaging().sendToDevice(token, payload, options)
  .then(pushResponse => {
    console.log("Successfully sent message:", pushResponse);
  })
  .catch(error => {
    console.log("Error sending message:", error);
  });
}

詳細! SwiftUI iPhoneアプリ開発入門ノート iOS 13 + Xcode11対応

詳細! SwiftUI iPhoneアプリ開発入門ノート iOS 13 + Xcode11対応

【Swift】FirestoreのSnapshotListenerをObservableにした場合のListenerのDetach

firebase.google.com

上記の公式ドキュメントを参考にクエリによる条件一致に該当するスナップショットのリスナーをObservableにした場合、以下のような実装になります。

続きを読む

新潟モバイルアプリカンファレンスの裏側の人の裏側

こんにちは。ABEChanです。あ、これから先私の呼称はABEChan/アベチャンでお願いします。

違う呼称で呼んだ方にはもしかしたら、私のことをよく知ってもらえてないのかな?と言う理由で焼肉をごちそうしてもらうかもです。←冗談です。

ついこの間新潟でモバイルアプリに関するカンファレンス・イベントを開催しました。

続きを読む

【Python】AWSLambda上でSelenium+ChromeDriver+headless-chromeを使ってクローリングする

yutaabe200.hatenablog.com

この記事の通り、Golang+Agouti+ChromeDriver+headless-chromeを使ってAWSLambda上で動かすことに失敗してしまったので、諦めてPythonを使用することにしました。

なお、こちらの方法はGolangで行う際より参考記事が豊富ですが、私自身Python自体も未経験でしたので、Pythonの使用手順から備忘録を兼ねて記載します。

続きを読む

【Golang】AWSLambda上でchromedriver+headless-chromeでクローリングできない[未解決]

最近とあるサイトのユーザーアクションを定期的に自動化したい事項がありました。

続きを読む

個人事業主初日

2019年4月1日から開業して個人事業主とめでたく(?)なったわけですが、色々雑に書いていきます。

屋号名について

特に意味はありません。好きなワインの名前からとりました。

あ、僕に送りつけてくれても良いです。

www.amazon.jp

事業計画・内容に関して

一応届出上は「システム・ソフトウェア開発」ですが特にないです。 開業して独立したいわけではないので。

なんでなったの?

1番の理由は経費を差し引いても雑収入が年間20万円を超えてしまい、確定申告を余儀なくされたのが理由です。 そして雑収入と言っても開業届けを出してしまえば事業所得として扱えそうなものもあったので都合が良かったのです。 それで青色申告をすれば何かと還付金が受けれるかもとか節税対策ぐらいの気持ちです。 元々簿記に関しては2級ならありましたし、FPの資格取った時もこの辺の知見はあったのと、割とお金周りの計算は好きでした。お金が好きなので。

あとは、後々フリーランスは視野に入れていて近々やってみたいとも思っているので、その準備と言うか、、、いざやるとなった時の為に手続きを出来るだけ今のうちにしておいたって感じです。

手続き自体は会計freeeなどにお世話になってすんなりできたのですが、それ以外の事業用カード作ったり通帳作ったり、どうせだから今の収入源をもう少し事業ぽくしてみたりとその辺が割と煩わしい作業が多い感じですがなんとかやり終えました。

で、何が言いたいの?

www.amazon.jp

...これ「屋号名」なのでプレゼントお待ちしてます。

【Golang】AWSLambdaからS3にアップロードする

yutaabe200.hatenablog.com

こちらで、Goのコード上からAWS S3にアップロードする方法を紹介しましたが、これをLambda関数化して同様に実行するとBodyHashErrorが起こります。

続きを読む

AWS Lambda の関数をAWS APIGatewayで発行したエンドポイントで実行させる

f:id:ka0in:20190311131435p:plain AWS Lambdaにgolang製の関数を設定し、トリガーにAWS APIGateway設定しエンドポイントを発行、そのエンドポイントのコールで関数を実行するまでの手順です。

続きを読む

GAE Cron+Pub/Sub+Cloud FunctionsによるFirestoreのバックアップ定期実行

yutaabe200.hatenablog.com

こちらの記事でバックアップ自体はgcloudコマンドで実行できることがわかりました。

本記事はこれを定期実行する為の手順を説明します。

続きを読む

Firestoreのバックアップ(gcloud編)

Google Cloud SDKをインストール

$ brew cask install google-cloud-sdk

続きを読む

今更だけどCodableにハマったところ(備忘録)

※色々省略しています。

struct Hoge: EntityProtcol, Codable {
    
    let uid: String
    let hogeName: String
    let firstHoges1: [String: String?]
    let secondHoges1: [String: String?]  
    let thirdHoges1: [String: String?]
    
    init(uid: String,
         hogeName: String,
         firstHoges1: [String: String?],
         secondHoges1: [String: String?],
         thirdHoges1: [String: String?]) {
        
        self.uid = uid
        self.hogeName = hogeName
        self.firstHoges1 = firstHoges1
        self.secondHoges1 = secondHoges1
        self.thirdHoges1 = thirdHoges1
    }
    
    static func deserialize<T: EntityProtcol>(document: [String: Any]) -> T {
        let json = try! JSONSerialization.data(withJSONObject: document, options: [])
        let decoder = JSONDecoder()
        decoder.keyDecodingStrategy = .convertFromSnakeCase
        return try! decoder.decode(JSSchoolExamEntity.self, from: json) as! T
    }
}
続きを読む