FirebaseのCloudFunctionsでPythonが公式サポートされたので、Flutterで試してみる。

# Cloud Functions for Firebase 2nd Genが正式版に
2023年5月10日(現地時間。JSTでは5/11の2:00)に行われたGoogle I/O 2023の中で発表されたうち、気になったニュースがありました。CloudFunctionsでPythonがサポートされるようになったとのこと。

これまでFunctionsを使ったことがなかったのですが、ちょうど必要になったので「ラッキー！」と思い、試してみることにしました。pythonも書けるアプリエンジニアになりたいっ！

https://www.youtube.com/watch?v=EIA58FKrA8Y

# 目的
今回の実装目的は「特定のグループに所属するユーザーにPush通知を送る」というもの。

私はDS（任天堂の古いゲーム）で遊ぶことのできた「ピクトチャット」を参考にしたアプリを開発しています。チャットルームで更新があった時にPush通知が飛ぶような仕組みを作りたいと思い、Functionsを活用することにしました。


### 前提条件
・環境構築済み（Flutter,Python,Firebase,npm等）
・Firebaseのプロジェクトと連携済み
・Push通知周りの実装については割愛

# 導入手順
### アプリ側（Flutter）
#### 1.ライブラリを導入
このコマンドを叩く。

    flutter pub add cloud_functions
     
あるいは以下をpubspeck.yamlに設定。

    dependencies:
      cloud_functions: ^4.3.0

#### 2. Functionを呼び出す処理を作る

    import 'package:cloud_functions/cloud_functions.dart';
    import 'package:flutter/foundation.dart';

    /// Cloud Functions
    final functions = FirebaseFunctions.instance;

    /// Push通知を送信
    Future<void> executePushNotificationByTopic({
      required String topic,
    }) async {
      try {
        await functions.httpsCallable('send_push_notification').call({
          "topic": topic,
        });
      } on FirebaseFunctionsException catch (error) {
        if (kDebugMode) {
          print('ーーーーーーCloudFunctionsErrorーーーーーー');
          print(error.code);
          print(error.details);
          print(error.message);
          print('ーーーーーーーーーーーーーーーーーーーーーーー');
        }
      }
    }


これでアプリ側の準備はOKです！
では、次はFunctionsの用意を進めましょう。

### Firebase側
https://firebase.google.com/docs/functions/get-started?hl=ja
#### 1.Node.js と Firebase CLI を設定する
任意の空ファイルを用意し、以下のコマンドで初期化。
これによりfirebaseのコマンドがインストールされます。

    npm install -g firebase-tools

#### 2. プロジェクトの初期化

    firebase login

    firebase init

initした際に、***javascript***,***typescript***,***python***のどれを使用するか聞かれるので、Pythonを選択。

#### 3.関数を作成
初期化したファイルのうち、main.pyを開いて関数を作成してください。
私の場合はPush通知が送りたいので、以下のようなメソッドを作成しました。

    from firebase_admin import messaging
    from firebase_functions import https_fn
    from firebase_admin import initialize_app
    
    initialize_app()
    
    @https_fn.on_call()
    def send_push_notification(req) -> https_fn.Response:
        # トピックを取得
        topic = req.data["topic"]
        if topic:
            message = messaging.Message(
                notification=messaging.Notification(
                    title='新しいてがきのメッセージが届いています！',
                    body='アプリをひらいて確認してみましょう！'
                ),
                data={
                    "custom_key": "custom_value"
                },
                topic=topic
            )
            response = messaging.send(message)
            raise response
        else:
            response_data = https_fn.HttpsError(
                code=https_fn.FunctionsErrorCode.INVALID_ARGUMENT,
                message=(
                    '送信先が指定されていません。'
                ),
            )
            raise response_data
#### 4.Firebaseへdeploy
関数を書き終えたら、最後の工程。Firebaseへデプロイ処理を行います。

    firebase deploy

ここまで出来たらあとはアプリから呼び出すだけです！

## まとめ
開発したアプリの一覧はこちらから！

https://lit.link/taminaryousuke

今回作ったアプリ

https://apps.apple.com/jp/app/%E6%89%8B%E6%9B%B8%E3%81%8D%E3%83%81%E3%83%A3%E3%83%83%E3%83%88-%E3%83%94%E3%82%AF%E3%83%88%E3%83%BC%E3%82%AF-%E6%83%B3%E3%81%84%E3%82%92%E5%B1%8A%E3%81%91%E3%82%8B-%E3%81%A4%E3%81%AA%E3%81%8C%E3%82%8B%E3%82%A2%E3%83%97%E3%83%AA/id6449237575

https://play.google.com/store/apps/details?id=com.tamina.hand_writing_chat

一番バズったアプリは「ガチャメーカー」です！

https://apps.apple.com/jp/app/%E3%82%AC%E3%83%81%E3%83%A3%E3%83%A1%E3%83%BC%E3%82%AB%E3%83%BC-%E3%81%82%E3%81%AA%E3%81%9F%E3%81%A0%E3%81%91%E3%81%AE%E3%82%AA%E3%83%AA%E3%82%B8%E3%83%8A%E3%83%AB%E3%82%AC%E3%83%81%E3%83%A3%E3%82%92%E4%BD%9C%E3%82%8B%E3%82%A2%E3%83%97%E3%83%AA/id1617490189

Twitterもやってます。
https://twitter.com/minnasinario


### 参考URL

https://zenn.dev/ail/articles/918c9d7d174d20

https://firebase.google.com/docs/functions/callable?hl=ja

https://github.com/firebase/functions-samples/blob/main/Python/quickstarts/callable-functions/main.py