はじめに
IonicでFirebaseを用いたプッシュ通知の実装について説明します。
プラットフォームはiOSです。
Ionicを使ったハイブリッドアプリ開発についてはこちら
動作環境
- macOS High Sierra 10.13.3
- ionic 3.20.0
- cordova 8.0.0
- Node v8.9.4
- npm 5.6.0
- Xcode 9.3 Build version 9E145
- cordova-plugin-firebase 1.0.0
- cordova-ios 4.5.4
Firebaseプロジェクトを作成
Firebase ConsoleでIonicプロジェクト用のFirebaseプロジェクトを追加します。
Firebaseプロジェクトにプッシュ通知を許可
プッシュ通知用の証明書(AuthKey)をFirebaseプロジェクトにアップロード
プッシュ通知に必要な証明書を発行します。
尚、Apple Developer Accountの作成及びリリースに必要な各種証明書は取得されているものとします。
まずCertificates, Identifiers & Profilesにアクセスします。
Keys>Allを開き、右上の+ボタンからKeyを作成します。
Key Descriptionで適当なNameを設定し、Key ServicesでAPNsにチェックを入れてContinueをクリックします。

確認を求められるので、Confirmで確定します。

AuthKeyファイルがダウンロード可能になるので、ローカル環境にダウンロードします。

FirebaseプロジェクトにiOSを追加
プロジェクトの左メニューからCloud Messagingを選び、アプリを追加します。
バンドルIDとニックネームを入力し、アプリの登録を実行します。

GoogleService-Info.plistファイルをダウンロードしておきます。(後にIonicプロジェクトと連携するために必要)

ダウンロードが完了したら、続行と終了で次に進みます。
AuthKeyファイルのアップロード
プロジェクト左メニューのProject Overviewの右にある設定アイコンからプロジェクトの設定画面に行きます。
クラウドメッセージングタブに移動し、[iOS アプリの設定]のAPNs 認証キーに先ほどのAuth Key(AuthKey_{ユニークキー}.p8ファイル)をアップロードをクリックします。

キーIDには先ほどCertificates, Identifiers & Profilesで作成したKeys>AllからKey IDを取得して入力します。
App ID PrefixにはDeveloper AccountのTeam IDが必要なので、Developer AccountからMembershipへ移動してTeam IDを確認して入力して下さい。

ここまで設定完了したらアップロードを実行します。
Ionicプロジェクトとの連携
CordovaでFirebaseを扱うプラグインを追加
Ionicプロジェクトは既に作成済みとします。
Ionicプロジェクトの作成方法についてはこちら
Cordovaでfirebaseを扱うプラグインをIonicプロジェクトに導入します。
$ cd SampleProject/ $ ionic cordova plugin add cordova-plugin-firebase@1.0.0 --save $ npm install --save @ionic-native/firebase
package.jsonにcordova-plugin-firebaseが追加されていれば問題ないです。
デバイストークン発行ロジックの追加
Firebaseから各デバイスにプッシュ通知を送るにはデバイスを識別するためのトークンが必要になります。
デバイストークンはアプリ起動時にFirebaseと通信することにより発行することができます。
そのためIonicプロジェクトの初期化処理の中でFirebaseのデバイストークンを発行する処理を実装していきます。
その前にまず先ほどダウンロードしたGoogleService-Info.plistファイルをIonicのプロジェクトのルートディレクトリにコピーします。
このプロパティリストを読み込むことで自身のFirebaseプロジェクトと連携することができます。
まずapp.module.tsファイルを開き、providersにFirebeseを追加します。
SampleProject/src/app/app.module.ts
.
import { Firebase } from '@ionic-native/firebase'; // Firebaseをインポート
.
.
.
@NgModule({
declarations: [
MyApp,
HelloIonicPage,
ItemDetailsPage,
ListPage
],
imports: [
BrowserModule,
IonicModule.forRoot(MyApp),
],
bootstrap: [IonicApp],
entryComponents: [
MyApp,
HelloIonicPage,
ItemDetailsPage,
ListPage
],
providers: [
StatusBar,
SplashScreen,
Firebase, // Firebaseを追加
{provide: ErrorHandler, useClass: IonicErrorHandler}
]
})
次にapp.component.tsファイルのinitializeApp()の中に、デバイストークンを払い出す処理を追加します。
プラットフォームがcordovaの場合のみ実行している理由は、ブラウザでの実行だとエラーとなるためです。
SampleProject/src/app/app.component.ts
import { Component, ViewChild } from '@angular/core';
import { Platform, MenuController, Nav } from 'ionic-angular';
import { HelloIonicPage } from '../pages/hello-ionic/hello-ionic';
import { ListPage } from '../pages/list/list';
import { StatusBar } from '@ionic-native/status-bar';
import { SplashScreen } from '@ionic-native/splash-screen';
import { Firebase } from '@ionic-native/firebase'; // Firebaseをインポート
@Component({
templateUrl: 'app.html'
})
export class MyApp {
@ViewChild(Nav) nav: Nav;
// make HelloIonicPage the root (or first) page
rootPage = HelloIonicPage;
pages: Array<{title: string, component: any}>;
constructor(
public platform: Platform,
public menu: MenuController,
public statusBar: StatusBar,
public splashScreen: SplashScreen,
private firebase: Firebase, // Firebaseを追加
) {
this.initializeApp();
// set our app's pages
this.pages = [
{ title: 'Hello Ionic', component: HelloIonicPage },
{ title: 'My First List', component: ListPage }
];
}
initializeApp() {
this.platform.ready().then(() => {
// Okay, so the platform is ready and our plugins are available.
// Here you can do any higher level native things you might need.
this.statusBar.styleDefault();
this.splashScreen.hide();
// ブラウザ実行の場合は処理しない
if (this.platform.is('cordova')) {
// Firebaseに対してデバイストークンの発行をリクエスト
this.firebase.getToken()
.then(token => {
// ここで取得したトークンをDBへ保存する
console.log(`The token is ${token}`)
}) // save the token server-side and use it to push notifications to this device
.catch(error => console.error('Error getting token', error));
this.firebase.onTokenRefresh()
.subscribe((token: string) => console.log(`Got a new token ${token}`));
}
});
}
openPage(page) {
// close the menu when clicking a link from the menu
this.menu.close();
// navigate to the new page if it is not the current page
this.nav.setRoot(page.component);
}
}
これでIonicプロジェクトからFirebase経由でデバイストークン取得する実行が完了しました。
設定が完了したら再度ビルドを実行して下さい。
$ ionic cordova build ios --prod
XcodeからNotification設定を有効化
XcodeプロジェクトのNotification関連の設定を有効にするためにxcodeprojファイルを開きます。
SampleProject>platforms>ios>SampleProject.xcodeproj
CapabilitiesタブのPush Notificationsでプッシュ通知をONにします。
もしエラーが出る場合はGeneralのIdentityでBundle Identifierを、SigningでTeamを正しく設定すれば上手く行くはずです。

次は実際に端末から起動してプッシュ通知を送ってみましょう!
Firebaseコンソールからのプッシュ通知
Firebase Cloud Messagingサービスでプッシュ機能を使う場合、2つの方法があります。
ひとつはコンソール画面からGUIでプッシュを送る方法。もうひとつはAPI経由でプッシュさせる方法です。
まずはコンソール画面から送ってみましょう。
左メニューのCloud Messagingサービスを選択して、「最初のメッセージを送信」をクリックします。

ここで通知に含まれるメッセージを入力し、アプリを選択します。
ここのアプリにはXcodeのBundle Identifierを設定して下さい。
また、詳細オプションやターゲットを設定すると通知をカスタマイズすることが出来ます。
設定が完了したら、「メッセージを送信」を実行します。

実機にプッシュ通知が届きました。(この時、あらかじめ実機にアプリをインストールさせておいてください)

一定ユーザにまとめて通知を送りたい場合に重宝しそうな機能です。
バックエンドから任意のタイミングでプッシュ通知
コンソールからのプッシュ通知は簡単で便利ですが、アプリの要件によってはユーザ毎に任意のタイミングでプッシュ通知を送りたいというニーズがあります。
ここではPythonスクリプトで任意のデバイスに向けてプッシュ通知を送ってみます。
Firebase Cloud Messaging APIとの連携
まずFirebase Cloud Messaging APIを利用するために、サービスアカウント用の秘密鍵ファイルを生成します。
Firebaseコンソールで、プロジェクト設定 > サービス アカウント 画面を開きます。
[新しい秘密鍵の生成をクリックします。

[キーを生成] をクリックしてスクリプトと同じディレクトリ保存します。

Firebase Cloud Messaging APIへのリクエスト
Firebase Cloud Messaging APIに向けたHTTPリクエストを生成します。
HTTP POSTリクエストのフォーマットは以下の通りです。
- HTTP POSTリクエスト
POST https://fcm.googleapis.com/v1/projects/myproject-XXXXX/messages:send HTTP/1.1
Content-Type: application/json
Authorization: Bearer ya29.ElqKBGN2Ri_Uz...HnS_uNreA // OAuth2.0アクセストークン
{
"message":{
"token" : "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...", // デバイストークン
"notification" : {
"body" : "This is an FCM notification message!",
"title" : "FCM Message",
}
}
}
エンドポイントはFirebaseプロジェクトIDを含むURLになります。
HeadersにOAuth2.0アクセストークンをセットし、Bodyにはメッセージのパラメータを渡します。
ここで指定するデバイストークンは実機でのアプリ起動時に取得したトークンです。サクッと動作確認をしたいだけであればページにトークンを表示させてそれを使う方法もあります。
実運用のときはDBで管理するのが順当でしょう。
OAuth2.0アクセストークンの取得
Googe APIにアクセスするためにはOAuth2.0という認証プロトコルを使用するのが方針のようです。
そのため上のリクエストに含めるアクセストークンの取得方法について見ていきます。
Google APIを操作するためのクライアントライブラリが様々な言語に提供されています。ここではPython用のライブラリを使用します。
OAuth2.0用のライブラリをインストールします。
$ pip install oauth2client
秘密鍵が格納されたファイルを読み込んでアクセストークンを取得するロジックです。
アクセストークンには有効期限があり、リクエスト毎に変わってしまうので、逐次取得し直す実装にすべきでしょう。
FCM_SCOPE = 'https://www.googleapis.com/auth/firebase.messaging'
def _get_access_token():
"""Retrieve a valid access token that can be used to authorize requests.
:return: Access token.
"""
credentials = ServiceAccountCredentials.from_json_keyfile_name('./myapp-XXXXXX.json', FCM_SCOPE) // 秘密鍵が格納されたファイル
access_token_info = credentials.get_access_token()
print("Access Token: " + access_token_info.access_token)
print("Expire: " + str(access_token_info.expires_in))
return access_token_info.access_token
それでは実際にOAuth2.0アクセストークンをHTTPリクエストヘッダーに追加し、Firebase Cloud Messaging API経由でメッセージをリクエストして見ます。
以下がスクリプト全文です。
import json
from oauth2client.service_account import ServiceAccountCredentials
import requests
FCM_SCOPE = 'https://www.googleapis.com/auth/firebase.messaging'
def _get_access_token():
"""Retrieve a valid access token that can be used to authorize requests.
:return: Access token.
"""
credentials = ServiceAccountCredentials.from_json_keyfile_name('./myapp-XXXXXX.json', FCM_SCOPE) // 秘密鍵が格納されたファイル
access_token_info = credentials.get_access_token()
print("Access Token: " + access_token_info.access_token)
print("Expire: " + str(access_token_info.expires_in))
return access_token_info.access_token
if __name__ == '__main__':
token = _get_access_token()
url = 'https://fcm.googleapis.com/v1/projects/myapp-XXXXX/messages:send' // FirebaseプロジェクトIDをURLに指定
headers = {
'Content-Type': 'application/json; UTF-8',
'Authorization': 'Bearer ' + token
}
data = {
"message":{
"token" : "ex9UW5T03sI:APA91bG9YMIAmYtzzty60kph8cnTxOJGkgi...", // プッシュ先の端末のデバイストークン
"notification" : {
"body" : "This is an FCM notification message!",
"title" : "Hello!"
}
}
}
response = requests.post(url, headers=headers, data=json.dumps(data))
print(response.status_code)
print(response.text)
まず最初にアクセストークンを取りに行き、そのトークンをheadersにセットしてFirebase Cloud Messaging APIにリクエストしています。
HTTPリクエストにrequestsモジュールを使用していますが、特に何を使っても問題ありません。
requestsモジュールのインストール方法は以下のコマンドです。
$ pip install requests
このスクリプトが成功すると端末にプッシュ通知が届きます。
もし何らかの理由でリクエストに失敗した場合は、Google APIsでプロジェクトのFirebase Cloud Messaging APIが有効になっているかどうか確認してください。
最後に
クライアントとバックエンド、APNsとFirebaseなど、連携するサービスが多いのでどうしても手順が複雑になりますが、コンソールからの実行とAPI経由の実行両方に対応できるのでかなり使えると思います。
アプリのグロースにはプッシュ通知は欠かせないので、通知周りを簡単に実行できるFirebaseには今後も期待してます。