2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

FirebaseFunctionsでPush通知をHTTP関数呼び出しで送信する

Last updated at Posted at 2020-02-06

はじめに

今回はFirebaseFunctionsのhttp関数を使用して特定のデバイスにPush通知を送る実装を行なっていきます。実装前に下記の準備項目が必要になります。

事前準備

  • Firebaseプロジェクト
  • Firebase/Messaging導入済のiosプロジェクト
  • APNsのFirebaseアップロード

FirebaseCLIインストール

まずは、FirebaseCLIをインストールすることでFunctionsのDeployやプロジェクトの切り替えなどをCLIで操作できるようにします。今回はnpmでインストールを行います。

npmインストール

とりあえず最新のものをnodebrewで取得してきてPathを通すとこまで終わらせます。

    $ brew install nodebrew
    $ nodebrew install-binary 13.7.0
    $ nodebrew use  v7.0.0
    $ echo 'export PATH=$HOME/.nodebrew/current/bin:$PATH' >> ~/.bash_profile
    $ source ~/.bash_profile

firebase-toolsインストール

1.npmでfirebase-toolsをインストールします。

    $ npm install -g firebase-tools

2.firebase-toolsコマンドを使用して、操作を行うユーザの認証をします。下記のコマンドを実行するとWebブラウザが立ち上がるので、Firebaseプロジェクトで編集権限のあるアカウントでログインを行います。

    $ firebase login

3.firebaseのプロジェクトをuseコマンドを使って指定します。この操作によりfirebase/functionsなどのデプロイ先を変更できたりします。

    $ firebase use firebase_project_id

Functionsプロジェクト作成

今回はFunctionsのみ使用するので下記のコマンドでプロジェクトを立ち上げます。

    $ firebase init functions

すると下記のような構造のプロジェクトが立ち上がるので、主にindex.jsを編集して関数を作成して行きます。
スクリーンショット 2020-02-05 22.25.27.png

参照: https://firebase.google.com/docs/functions/get-started?hl=ja

FirebaseAdminSDKインストール

1.sdkの情報などを保存するpackage.jsonを作成します。

    $ npm init

2.firebase-admin npmパッケージをインストールします。

   $ npm install firebase-admin --save

3.次にfirebase-adminを初期化をするためにローカルの環境変数にFirebaseサービスアカウントの秘密鍵を生成したファイルへのパスを指定します。これを設定することでSDKの初期化時にキーが参照され、プロジェクトでの認証が完了します。CIなどでブランチごとにDeploy先を変更させたい時はどうやって秘密鍵を参照させるのがベストなんでしょうか?

   $ export GOOGLE_APPLICATION_CREDENTIALS="/home/user/Downloads/service-account-file.json"

参照: https://firebase.google.com/docs/admin/setup?hl=ja

4.index.jsに移動してsdkの初期化コードを追加します。

index.js
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();

node.jsの実装

index.js
const functions = require('firebase-functions');
const admin = require('firebase-admin');

admin.initializeApp();

//onRequestでhttpからの呼び出しを可能にします。
exports.push = functions.https.onRequest((request, response) => {
  if (request.query.device_token !== undefined && request.body.message !== undefined) {
    const device_token = request.query.device_token
    const message = request.body.message
    const payload = {
      notification: {
        body: message,
        badge: "1",
        sound:"default",
      }
    };
    switch (request.method) {
      case 'POST':
        push(device_token, payload, response);
        break
      default:
        response.status(400).send({ error: 'Invalid request method' })
        break
    }
  } else {
    response.status(400).send({ error: 'Invalid request parameters' })
  }
})

function push(token, payload, response) {

  const options = {
    priority: "high",
  };

  //FCMにAdminSDKを介してPush通知を送信します。
  admin.messaging().sendToDevice(token, payload, options)
  .then(pushResponse => {
    console.log("Successfully sent message:", pushResponse);
    response.status(200).send({message: 'Successfully sent message'})
  })
  .catch(error => {
    response.status(400).send({ error: 'Error sending message' })
  });
}

swiftの実装

AppDelegate.swift
import UIKit
import Firebase
import UserNotifications
import FirebaseMessaging

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?
    private var mainTabViewController: MainTabViewController?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        //環境ごとにプロジェクトを変えてるためplistを変更しています。
        let filePath = Bundle.main.path(forResource: Config.Server.instance.firebaseInfoPlistName, ofType:"plist")
        //Forced Unwrapping🚨
        FirebaseApp.configure(options: FirebaseOptions(contentsOfFile:filePath!)!)
        initFirebaseMessaging()
        initRemoteNotification(application)
        window = UIWindow(frame: UIScreen.main.bounds)
        window!.makeKeyAndVisible()
        navigate()
        return true
    }

    func navigate(_ isTrial: Bool = false) {
        guard let window = window else {
            assert(false)
            return
        }
        let previousVC = window.rootViewController
        for v in window.subviews {
            v.removeFromSuperview()
        }
        let vc = MainTabViewController()
        mainTabViewController = vc
        window.rootViewController = vc
        if let previousVC = previousVC {
            previousVC.dismiss(animated: false) {
                previousVC.view.removeFromSuperview()
            }
        }
    }

    private func initRemoteNotification(_ application: UIApplication) {
        UNUserNotificationCenter.current().delegate = self

        let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
        //TODO: Relocate requestAuthorization method.
        UNUserNotificationCenter.current().requestAuthorization(options: authOptions, completionHandler: {_, _ in })
        application.registerForRemoteNotifications()
    }

    private func initFirebaseMessaging() {
        //DelegateでdeviceTokenの変更を監視します。
        Messaging.messaging().delegate = self
        //明示的にdeviceTokenを取得します。
        InstanceID.instanceID().instanceID { (result, error) in
          if let error = error {
            //TODO: Error handling.
            print("Error fetching remote instance ID: \(error)")
          } else if let result = result {
            //TODO: Send token to parnovi api for update user fcm token. if authorized == true
            print("Remote instance ID token: \(result.token)")
          }
        }
    }
}

extension AppDelegate: UNUserNotificationCenterDelegate {
    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        completionHandler([.badge, .sound, .alert])
    }

    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
        completionHandler()
    }
}

extension AppDelegate: MessagingDelegate {
    //Observe firebase messaging token.
    func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
    }
}

FunctionsのDeploy

実際に関数をデプロイしてPush通知を送信してみます。

    $ firebase deploy --only functions

swiftのInstanceID.instanceID().instanceID で取得したDeviceTokenを使ってcurlで実際にPushを送信してみます。

    $ curl -X POST https://yout-functions-url/push?device_token=your-device-token -d "message=I love fishing🎣"

結果

IMG_0964.jpg

さいごに

今回はテスト的に実行できるようにするため、httpリクエストに認証は設定していませんでしたが、また実装し直したら編集しようと思います。また、CIなどを使ってfirebase/functionsなどをデプロイするとき、どのようにFirebaseプロジェクトの秘密鍵を参照させるのがベストなのでしょうか。。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?