JavaScript
HTML5

Progressive Web Appを作ってみる #Push Message編

More than 3 years have passed since last update.


前回

この記事は、前回の記事の続きになります。


Push通知

ServiceWorker(以下、SW)は、ブラウザのバックグラウンドで動作していることから、Push通知のイベントを受け取った際に、発火させメッセージを表示させることも可能になります。

それでは、実際に作っていきましょう


動作環境


  • SWを利用するので、https必須です。

  • localhost環境でもPush通知を送り受け取ることが可能です。

  • Chromeのシークレットウィンドウでは動作しません


Google Developer Console

まずはPush通知を送ってくれるサービスに登録する必要がありますので、 Goole Developer Consoleに登録し、新規プロジェクトを作成します。

5a526f9da97258d035f73051490cdc2b.png

プロジェクトを登録すると以下の様な見た目になると思います。

次に「Google APIを利用する」をクリックします

a28f6a42f953431a08716aec5190995d.png

「Mobile API」から「Cloud Message for Android」を選択してAPIを有効にします。

b25b4b8125bae3caa83e122f58814bee.png

次に、「認証情報」から「新しい認証情報 > APIキー > サーバー キー」と進んでKEYを作成しメモしておきます。

77123b181ab150693b365db287cf6a29.png

作成したKEYをメモしておきます。

ダッシュボードに戻って、プロジェクトに表示されている「プロジェクト番号」をメモしておきます

6da746502688afdbfa01ae9b2694bb92.png


manifest.jsonの作成

manifest.jsonファイルを作成します


manifest.json



{
"name": "My Portfolio",
"short_name": "MYP",
"icons": [{
"src": "images/touch/icon-128x128.png",
"sizes": "128x128",
"type": "image/png"
}, {
"src": "images/touch/apple-touch-icon.png",
"sizes": "152x152",
"type": "image/png"
}, {
"src": "images/touch/ms-touch-icon-144x144-precomposed.png",
"sizes": "144x144",
"type": "image/png"
}, {
"src": "images/touch/chrome-touch-icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
}],
"start_url": "/index.html",
"display": "standalone",
"background_color": "#3E4EB8",
"theme_color": "#2F3BA2",
"gcm_sender_id": <<プロジェクト番号>>
}

manifest.jsonの「gcm_sender_id」にメモしておいたプロジェクト番号を追記します


Service Worker

事前に以下のような通知の許諾ボタンを作成しておきます。

1257a8724e6667143603b6bea90138d9.png

それでは前回作成したswにpush通知部分を追記していきます。

まずは、許諾ボタンの初期化をする部分です


index.js

    function initialiseState() {

if (!('showNotification' in ServiceWorkerRegistration.prototype)) {
console.warn('プッシュ通知が対応されておりません');
return;
}

if (Notification.permission === 'denied') {
console.warn('通知をブロックしております');
return;
}

if (!('PushManager' in window)) {
console.warn('プッシュ通知が対応されておりません');
return;
}

navigator.serviceWorker.ready.then(function(serviceWorkerRegistration) {
serviceWorkerRegistration.pushManager.getSubscription().then(
function (subscription) {

pushEnableButton.disabled = false;
pushDisableButton.disabled = false;

pushEnableButton.classList.add("non-active");
pushEnableButton.disabled = false;

pushDisableButton.classList.remove("non-active");
pushDisableButton.disabled = true;

if (!subscription) {
return;
}

// 取得したsubscriptionをServerなどで保存させる処理
sendSubscriptionToServer(subscription);

pushEnableButton.classList.remove("non-active");
pushEnableButton.disabled = true;

pushDisableButton.classList.add("non-active");
pushDisableButton.disabled = false;

})
.catch(function(err){console.warn('Error during getSubscription()', err); });
});
}


初期化処理では、「ブラウザがPush通知をサポートしているか」をチェックした後に、既に選択済みであった場合に、ボタンのスタイルを変更している処理をしています。

次に、初期化処理をSWに組み込みます


index.js

    if (

'serviceWorker' in navigator &&
(window.location.protocol === 'https:' || isLocalhost)
) {
navigator.serviceWorker.register('serviceWorker.js', {scope: '/'}).then(
function (registration) {
if (typeof registration.update == 'function') {
registration.update();
}

initialiseState();
})
.catch(function (error) {
console.error('Service Worker registration failed: ', error);
});
}


前回作成した、SW登録部分にて初期化処理を呼び出します。

次に、「受け取るボタン」を押されたイベントのコードです。


index.js

    pushEnableButton.addEventListener('click', function(){

Notification.requestPermission(function(permission) {
if(permission !== 'denied') {
subscribe();
}
else {
alert ('プッシュ通知を有効にできません。ブラウザの設定を確認して下さい。');
}
});
});

function subscribe() {
navigator.serviceWorker.ready.then(function(serviceWorkerRegistration) {
serviceWorkerRegistration.pushManager.subscribe({ userVisibleOnly: true }).then(
function(subscription) {
pushEnableButton.classList.remove("non-active");
pushEnableButton.disabled = true;

pushDisableButton.classList.add("non-active");
pushDisableButton.disabled = false;

return sendSubscriptionToServer(subscription);
}
)
.catch(
function (e) {
if (Notification.permission == 'denied') {
console.warn('Permission for Notifications was denied');
}
else {
console.error('Unable to subscribe to push.', e);
window.alert(e);
}
}
)
})
}

function sendSubscriptionToServer(subscription) {
console.log(subscription);
}


ボタンが押されたら、「通知の認証可否」「subscription」があるかをチェックします。subscriptionを取得したら、ボタンのスタイルを変更して、subscriptionを保存するためにサーバーに送ります(今回はコンソールに表示されるだけにしております)


※ userVilibleOnly:trueの指定が必須になりました。

※ subscriptionには、Push通知を送るときに必要なendpointの情報が含まれています


次に、「受け取らないボタン」を押されたイベントのコードです


index.js

    pushDisableButton.addEventListener('click', function(){

unsubscribled();
});

function unsubscribled() {
navigator.serviceWorker.ready.then(function(serviceWorkerRegistration) {
serviceWorkerRegistration.pushManager.getSubscription().then(
function(pushSubscription) {
if ( ! pushSubscription ) {
pushEnableButton.classList.add("non-active");
pushEnableButton.disabled = false;

pushDisableButton.classList.remove("non-active");
pushDisableButton.disabled = true;

return;
}

sendSubscriptionToServerForDelete(pushSubscription);

pushSubscription.unsubscribe().then(function(successful) {
pushEnableButton.classList.add("non-active");
pushEnableButton.disabled = false;

pushDisableButton.classList.remove("non-active");
pushDisableButton.disabled = true;
}).catch(function(e) {
console.error('Unsubscription error: ', e);

pushEnableButton.classList.add("non-active");
pushEnableButton.disabled = false;

pushDisableButton.classList.remove("non-active");
pushDisableButton.disabled = true;
});
}
)
.catch(
function(e) {
console.error('Error thrown while unsubscribing from push messaging.', e);
}
)
});
}

function sendSubscriptionToServerForDelete(subscrption) {
console.log('sending to server for delete:', subscrption);
}


sendSubscriptionToServerForDelete()で、サーバーに保存されたsubscriptionを削除するようにしております。また、ボタンのスタイルを変更しています。

次にプッシュ通知を受け取った際の処理を書いていきます。これはSWにインストールさせるjsファイルに書きます


serviceWorker.js

self.addEventListener('push', function(event) {

self.addEventListener('push', function(event) {
var title = 'メッセージが届きました';
var body = 'メッセージ内容は、こんにちわ!です';
var icon = '/images/touch/chrome-touch-icon-192x192.png';
var tag = 'simple-push-demo-notification-tag';

event.waitUntil(
self.registration.showNotification(title, {
body: body,
icon: icon,
tag: tag
})
);
});
});


push通知を受け取った場合「pushイベント」が発火します。その際に画面に通知を表示させるようにします。

次に、通知をクリックされたイベント処理を書きます。


serviceWorker.js

self.addEventListener('notificationclick', function(event) {

event.notification.close();

event.waitUntil(
clients.matchAll({
type: "window"
})
.then(function(clientList) {
for (var i = 0; i < clientList.length; i++) {
var client = clientList[i];
if (client.url == '/' && 'focus' in client)
return client.focus();
}
if (clients.openWindow) {
return clients.openWindow('/');
}
})
);
});


それでは、実際に通知を送ってみましょう。

通知を送るためにcurlを使用します。

$ curl --header "Authorization: key=<<SERVER_API_KEY>>" --header "Content-Type: application/json" https://android.googleapis.com/gcm/send -d"{\"registration_ids\":[\"<<REGISTRATION_ID>>\"]}"

SERVER_KEYには、GoogleDeveloperConsoleで作成メモしておいたKEYを書きます。registration_idsには、「受け取るボタン」を押した際に表示させたsubscription内のendpointから、URLを取り除いたものを指定します。

https://android.googleapis.com/gcm/send/APA91bHPffi8zclbIBDcToXN_LEpT6iA87pgR-J-MuuVVycM0SmptG-rXdCPKTM5pvKiHk2Ts-ukL1KV8exGOnurOAKdbvH9jcvg8h2gSi-zZJyToiiydjAJW6Fa9mE3_7vsNIgzF28KGspVmLUpMgYLBd1rxaVh-L4NDzD7HyTkhFOfwWiyVdKh__rEt15W9n2o6cZ8nxrP

ここから、

https://android.googleapis.com/gcm/send

を取り除いた

APA91bHPffi8zclbIBDcToXN_LEpT6iA87pgR-J-MuuVVycM0SmptG-rXdCPKTM5pvKiHk2Ts-ukL1KV8exGOnurOAKdbvH9jcvg8h2gSi-zZJyToiiydjAJW6Fa9mE3_7vsNIgzF28KGspVmLUpMgYLBd1rxaVh-L4NDzD7HyTkhFOfwWiyVdKh__rEt15W9n2o6cZ8nxrP

がREGISTRATION_IDになります

それでは、実際にcurlコマンドを叩いてみましょう

2e5cb14584f3fa2a8c3168f8796c5b02.png


まとめ

このようにSWを使うとウェブアプリからプッシュ通知を行なうことができます。新たに加わったカスタムボタンを使うと、通知にボタンを表示させることができるようになり、そのボタンをclickした時に任意の処理を行なうことが出来るみたいです。

次回は、web manifestを使った見た目の部分を書きたいと思います