はじめに
こんにちは!
去年に引き続き、今年も書きます。
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
さっそく実装に入りましょう 🤩
実装については、以下の流れで説明していきたいと思います!
- 必要なモジュールなどをインストール
- Push機能実装:アプリ側
- 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
は、固定値ですので、変更しないでください!
{
... 省略 ...
"gcm_sender_id": "103953800507" // ← gcm_sender_idを追加
... 省略 ...
}
2-2. メッセージサービスワーカーを作成
Pushメッセージには、 service worker
が必要になります。
まず、Firebaseで messagingSenderId
をコピーしておきましょう。
次は、src
ディレクトリの配下に firebase-messaging-sw.js
を作成します。
そして、↑でコピーしたmessagingSenderId
を、以下の firebase.initializeApp()
に貼りつけます!
// 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
を追加
{
... 省略 ...
"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で「鍵ペア」を生成
「プロジェクトの設定>クラウドメッセージング」に入って、鍵ペアを生成してください。
生成した鍵ペアをコピーしておいてください⭐️
2-4-2. Service作成
メッセージサービスは、Push通知メッセージを受け取るユーザーの許可を得る役割をします。
ng g service messaging
ざっくり以下のような構成になります!
getPermission()
- ユーザーからPush通知を行う許可を取得
updateToken()
- FirebaseにFCMトークンを保存
- ユーザーにPush通知を送る度にこのトークンを参照するので、Firebaseに保存する
receiveMessage()
- メッセージを受信
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を修正
// ... 省略 ...
import { MessagingService } from './service/messaging.service';
@NgModule({
// ... 省略 ...
providers: [
MessagingService, // ← を追加
]
})
export class AppModule {}
2-4-4. 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トークンを取得し、メッセージデータを用いてユーザーに送信するペイロードが作られます。
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
- https://angularfirebase.com/lessons/send-push-notifications-in-angular-with-firebase-cloud-messaging/#manifest-json
- https://firebase.google.com/docs/cloud-messaging/js/client?hl=ja
- https://firebase.google.com/docs/functions/get-started?hl=ja
- https://firebase.google.com/docs/cloud-messaging/js/send-multiple?hl=ja