LoginSignup
18
9

More than 5 years have passed since last update.

Angular + Firebase + PWAでPush通知機能を実装する

Last updated at Posted at 2018-12-22

はじめに

こんにちは!
去年に引き続き、今年も書きます。
Fusic Advent Calendar 2018 19日目の記事です!
今回は、「Angular + Firebase + PWAでPush通知機能を実装」についてまとめてみました 👏

やりたいこと

PWAでPush機能実装したい!

※ PWAって何?的な説明は、以下を見てください
- https://developers.google.com/web/fundamentals/codelabs/your-first-pwapp/?hl=ja
- https://developers.google.com/web/progressive-web-apps/

開発環境

  • Angular 6
  • Firebase
    • Realtime Database
    • Functions

さっそく実装に入りましょう 🤩

実装については、以下の流れで説明していきたいと思います!

  1. 必要なモジュールなどをインストール
  2. Push機能実装:アプリ側
  3. Push機能実装:Firebase Functions側

1. 必要なモジュールなどをインストール

Firebase CLIをインストール

yarn global add firebase-tools@latest

Firebase CLIの詳細については、こちらを見て下さい!

Firebaseモジュールをインストール

yarn add firebase --save

2. Push機能実装:アプリ側

2-1. manifest.jsonを修正

ハードコード値 gcm_sender_id を指定するウェブアプリ マニフェストを追加する必要があります。

「ブラウザ送信者 ID」と、Firebase プロジェクト設定に示されるプロジェクト固有の送信者 ID 値とを混同しないでください。
manifest.json のブラウザ送信者 ID は固定値で、すべての FCM JavaScript クライアントで共通です。

gcm_sender_idは、固定値ですので、変更しないでください!

src/manifest.json
{
  ... 省略 ...
  "gcm_sender_id": "103953800507" //  gcm_sender_idを追加
  ... 省略 ...
}

2-2. メッセージサービスワーカーを作成

Pushメッセージには、 service worker が必要になります。
まず、Firebaseで messagingSenderIdをコピーしておきましょう。
Untitled-4bc6eea9-3ccd-4c4e-a383-40bcef7df765.png
次は、srcディレクトリの配下に firebase-messaging-sw.js を作成します。
そして、↑でコピーしたmessagingSenderId を、以下の firebase.initializeApp() に貼りつけます!

src/firebase-messaging-sw.js
// Give the service worker access to Firebase Messaging.
// Note that you can only use Firebase Messaging here, other Firebase libraries
// are not available in the service worker.
importScripts('https://www.gstatic.com/firebasejs/5.5.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/5.5.0/firebase-messaging.js');

// Initialize the Firebase app in the service worker by passing in the
// messagingSenderId.
firebase.initializeApp({
  messagingSenderId: '' // ↑でコピーしたmessagingSenderIdを追加
});

// Retrieve an instance of Firebase Messaging so that it can handle background
// messages.
const messaging = firebase.messaging();

messaging.setBackgroundMessageHandler(function(payload) {
  const notificationTitle = payload.notification.title;
  const notificationOptions = {
    body: payload.notification.body,
    icon: payload.notification.icon
  };

  return self.registration.showNotification(notificationTitle, notificationOptions);
});

self.addEventListener('install', function(event) {
  console.log('Service Worker installing.');
});

self.addEventListener('activate', function(event) {
  console.log('Service Worker activating.');
});

これで、ユーザーがアプリを閉じても、アプリが新しいメッセージを検知することができます!

2-3. angular.jsonを修正

angular.jsonにmanifest.json, firebase-messaging-sw.js を追加

angular.json
{
  ... 省略 ...
  "projects": {
    "architect": {
      "build": {
        "options": {
          "assets": [
            "src/favicon.ico",
            "src/assets",
            "src/manifest.json", // ←追加
            "src/firebase-messaging-sw.js" // ←追加
          ]
        }
      }
    }
  }
}

2-4. メッセージサービスを作成

まずは、Firebaseの「鍵ペア」を生成する必要があります。

2-4-1. Firebaseで「鍵ペア」を生成

「プロジェクトの設定>クラウドメッセージング」に入って、鍵ペアを生成してください。
Untitled-7f78e992-b0c6-4fe3-8555-8167e9ff30eb.png
生成した鍵ペアをコピーしておいてください⭐️

2-4-2. Service作成

メッセージサービスは、Push通知メッセージを受け取るユーザーの許可を得る役割をします。

ng g service messaging

ざっくり以下のような構成になります!

getPermission()
  • ユーザーからPush通知を行う許可を取得
updateToken()
  • FirebaseにFCMトークンを保存
  • ユーザーにPush通知を送る度にこのトークンを参照するので、Firebaseに保存する
receiveMessage()
  • メッセージを受信
src/app/service/messaging.service.ts
import { Injectable } from '@angular/core';
import { AngularFireDatabase } from 'angularfire2/database';
import { AngularFireAuth } from 'angularfire2/auth';
import { BehaviorSubject } from 'rxjs';
import * as firebase from 'firebase';
import 'firebase/messaging';

@Injectable({
  providedIn: 'root',
})
export class MessagingService {
  messaging = firebase.messaging();
  currentMessage = new BehaviorSubject(null);
  constructor(
    private db: AngularFireDatabase,
    private afAuth: AngularFireAuth
  ) {
    // Add the public key generated from the console here.
    this.messaging.usePublicVapidKey(
      '' // <- ここに、上記で生成した鍵ペアを貼りつけてください
    );
  }

  updateToken(token) {
    this.afAuth.authState.subscribe((user) => {
      if (!user) {
        return;
      }

      const data = { [user.uid]: token };
      this.db.object('fcmTokens/').update(data);
    });
  }

  getPermission() {
    this.messaging
      .requestPermission()
      .then(() => {
        console.log('Notification permission granted.');
        return this.messaging.getToken();
      })
      .then((token) => {
        console.log(token);
        this.updateToken(token);
      })
      .catch((err) => {
        console.log('Unable to get permission to notify.', err);
      });
  }

  receiveMessage() {
    this.messaging.onMessage((payload) => {
      console.log('Message received. ', payload);
      this.currentMessage.next(payload);
    });
  }
}

2-4-3. app.module.tsを修正

src/app/app.module.ts
// ... 省略 ...
import { MessagingService } from './service/messaging.service';

@NgModule({
  // ... 省略 ...
  providers: [
    MessagingService, // ← を追加
  ]
})
export class AppModule {}

2-4-4. app.component.tsを修正

src/app/app.component.ts
// ... 省略 ...
import { MessagingService } from './service/messaging.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
  message;

  constructor(private msgService: MessagingService) {}

  ngOnInit() {
    this.msgService.getPermission();
    this.msgService.receiveMessage();
    this.message = this.msgService.currentMessage;
  }
  // ... 省略 ...
}

これで、以下のようにアプリにアクセスすると、「通知を許可しますか?」という確認アラートが表示されるようになります!🎉

3. Push機能実装:Firebase Functions側

3-1. Firebaseのfunctionsを初期化し、関数を作成する

firebase init functions

※ 作成の詳細手順については、こちらを見てください!


初期化すると、以下の構成になります!

myproject
 +- .firebaserc    # Hidden file that helps you quickly switch between
 |                 # projects with `firebase use`
 |
 +- firebase.json  # Describes properties for your project
 |
 +- functions/     # Directory containing all your functions code
      |
      +- package.json  # npm package file describing your Cloud Functions code.
      |
      +- tsconfig.json # Config file containing compiler options.
      |
      +- tslint.json   # Optional file containing rules for TypeScript linting.
      |
      +- src/     # Directory containing TypeScript source
      |   |
      |   +- index.ts     # main source file for your Cloud Functions code
      |
      +- lib/
          |
          +- index.js     # JavaScript output from TypeScript source.
          |
          +- index.js.map # Sourcemap file for TypeScript source.

ここからは、functions/src/index.ts を修正していきますー!

3-2. index.tsを修正

DBに更新がある度に、トリガーによって onCreate() が実行されます。
また、DBに保存されているFCMトークンを取得し、メッセージデータを用いてユーザーに送信するペイロードが作られます。

functions/src/index.ts
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
admin.initializeApp();

export const fcmSend = functions.database
  .ref('') // ←ここは、各自のDB構造に合わせて定義してください(例:/article/{articleId})
  .onCreate((snapshot, context) => {
    const articleInfo = snapshot.val();

    const payload = {
      notification: {
        title: '', // Pushメッセージのタイトル
        body: articleInfo.title + 'が登録されました 🎉', // Pushメッセージ本文
        clickAction: '', // Push通知をタップした時に、飛ばすURLを指定
        icon: '', // Push通知で使うロゴ
      },
    };

    admin
      .database()
      .ref('/fcmTokens/')
      .once('value')
      .then((token) => {
        const tokenList = token.val() || '';

        Object.keys(tokenList).forEach(function(key, index) {
          console.log(tokenList[key]);
          admin
            .messaging()
            .sendToDevice(tokenList[key], payload)
            .then((res) => {
              console.log('Sent Successfully', res);
            })
            .catch((err) => {
              console.log(err);
            });
        });
      })
      .catch((err) => {
        console.log(err);
      });
  });

これで実装完了です!
実際に、こんな感じでPush通知が飛んできます。

今回は、特定のユーザー一人一人にPush通知を送信していますが、複数ユーザーにメッセージを送信することもできるらしいので、こちらもぜひ参考にしてください!

まとめ

Firebase本当最高!👍👍👍

おまけ

今回の実装を含めた個人プロジェクトを開発し、運用しています。

このテーマで、FRONTEND CONFERENCE FUKUOKA2018でお話させていただきました。
スライドなどを公開してますので、興味ある方は是非見てください!

・テーマ:AngularとFirebaseで作ったPWA対応アプリで家庭内の課題を解決した話」
・公式プロフィール :https://frontend-conf.fukuoka.jp/sessions#c-6
・スライド:https://slides.com/lighthouse-dev/fec_fukuoka/#/

参考URL

18
9
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
18
9