OpenWeb でのプッシュ通知

開発者の部屋にウェブにないモバイル デバイスの機能を尋ねると、プッシュ通知は常に上位に位置します。

プッシュ通知を使用すると、ユーザーはお気に入りのサイトからタイムリーな最新情報を受け取ることができるため、カスタマイズされた魅力的なコンテンツでユーザーの再エンゲージメントを効果的に行うことができます。

Chrome バージョン 42 以降、デベロッパーは Push APINotification API を利用できます。

Chrome の Push API は、ウェブアプリ マニフェストService Worker など、いくつかのテクノロジーに依存しています。この投稿ではこれらのテクノロジーを一つずつ見ていきますが、プッシュ メッセージングを稼働させるのに最低限の技術しかありません。マニフェストの他の機能と Service Worker のオフライン機能について詳しくは、上記のリンクをご覧ください。

また、Chrome の今後のバージョンで API に追加される内容を確認し、最後によくある質問を紹介します。

Chrome 用のプッシュ メッセージの実装

このセクションでは、ウェブアプリでプッシュ メッセージングをサポートするために行う必要がある各手順について説明します。

Service Worker を登録する

ウェブ用の push メッセージを実装するための Service Worker の依存関係があります。これは、push メッセージを受信すると、ブラウザはページを開かずにバックグラウンドで実行される Service Worker を起動し、イベントをディスパッチできるため、その push メッセージの処理方法を決定できるからです。

以下に、ウェブアプリで Service Worker を登録する方法の例を示します。登録が正常に完了すると、initialiseState() が呼び出されます。これについては後ほど説明します。

var isPushEnabled = false;

…

window.addEventListener('load', function() {
    var pushButton = document.querySelector('.js-push-button');
    pushButton.addEventListener('click', function() {
    if (isPushEnabled) {
        unsubscribe();
    } else {
        subscribe();
    }
    });

    // Check that service workers are supported, if so, progressively
    // enhance and add push messaging support, otherwise continue without it.
    if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('/service-worker.js')
    .then(initialiseState);
    } else {
    console.warn('Service workers aren\'t supported in this browser.');
    }
});

ボタンクリック ハンドラは、プッシュ メッセージのサブスクライブまたはサブスクライブ解除を行います。isPushEnabled は、プッシュ メッセージが現在サブスクライブされているかどうかを単純に追跡するグローバル変数です。これらはコード スニペット全体で参照されます。

次に、push メッセージを処理するロジックを含む service-worker.js ファイルを登録する前に、Service Worker がサポートされていることを確認します。ここでは、この JavaScript ファイルがサイトの Service Worker であることをブラウザに伝えるだけです。

初期状態をセットアップする

Chrome でのプッシュ メッセージの UX の有効 / 無効の例。

Service Worker を登録したら、UI の状態を設定する必要があります。

ユーザーは、シンプルな UI でサイトのプッシュ メッセージを有効または無効にでき、変更があった場合に常に更新されることを期待します。つまり、ユーザーがサイトでプッシュ メッセージを有効にしてから 1 週間後に離脱した場合、プッシュ メッセージがすでに有効になっていることが UI でハイライト表示されます。

このドキュメントの UX ガイドラインをご覧ください。この記事では、技術的な側面に焦点を当てます。

この時点で、対処すべき状態は有効と無効の 2 つしかないとお考えかもしれません。ただし、通知に関して考慮すべき他の状態もいくつかあります。

Chrome におけるさまざまな考慮事項とプッシュ状態を示す図

ボタンを有効にする前にいくつかの API を確認する必要があります。すべてがサポートされている場合は、UI を有効にして初期状態を設定して、プッシュ メッセージングがサブスクライブされているかどうかを示すことができます。

これらのチェックの大部分では UI が無効になるため、初期状態を無効に設定する必要があります。また、JS ファイルをダウンロードできない、ユーザーが JavaScript を無効にしているなど、ページの JavaScript に問題が発生した場合の混乱も回避できます。

<button class="js-push-button" disabled>
    Enable Push Messages
</button>

この初期状態では、Service Worker の登録後に、上記のチェックを initialiseState() メソッドで実行できます。

// Once the service worker is registered set the initial state
function initialiseState() {
    // Are Notifications supported in the service worker?
    if (!('showNotification' in ServiceWorkerRegistration.prototype)) {
    console.warn('Notifications aren\'t supported.');
    return;
    }

    // Check the current Notification permission.
    // If its denied, it's a permanent block until the
    // user changes the permission
    if (Notification.permission === 'denied') {
    console.warn('The user has blocked notifications.');
    return;
    }

    // Check if push messaging is supported
    if (!('PushManager' in window)) {
    console.warn('Push messaging isn\'t supported.');
    return;
    }

    // We need the service worker registration to check for a subscription
    navigator.serviceWorker.ready.then(function(serviceWorkerRegistration) {
    // Do we already have a push message subscription?
    serviceWorkerRegistration.pushManager.getSubscription()
        .then(function(subscription) {
        // Enable any UI which subscribes / unsubscribes from
        // push messages.
        var pushButton = document.querySelector('.js-push-button');
        pushButton.disabled = false;

        if (!subscription) {
            // We aren't subscribed to push, so set UI
            // to allow the user to enable push
            return;
        }

        // Keep your server in sync with the latest subscriptionId
        sendSubscriptionToServer(subscription);

        // Set your UI to show they have subscribed for
        // push messages
        pushButton.textContent = 'Disable Push Messages';
        isPushEnabled = true;
        })
        .catch(function(err) {
        console.warn('Error during getSubscription()', err);
        });
    });
}

この手順の概要:

  • showNotification が ServiceWorkerRegistration プロトタイプで使用できることを確認します。これがないと、プッシュ メッセージの受信時に Service Worker からの通知を表示できません。
  • Google は、現在の Notification.permission が何であるかを確認し、"denied" でないことを確認します。権限が拒否された場合、ユーザーがブラウザで権限を手動で変更するまで通知を表示できません。
  • プッシュ メッセージングがサポートされているかどうかを確認するには、PushManager が window オブジェクトで使用できることを確認します。
  • 最後に、pushManager.getSubscription() を使用して、すでにサブスクリプションがあるかどうかを確認します。通知が届いた場合は、購読の詳細をサーバーに送信して正しい情報があることを確認し、プッシュ メッセージがすでに有効かどうかを示すように UI を設定します。サブスクリプション オブジェクトに存在する詳細情報については、この記事の後半で説明します。

navigator.serviceWorker.ready が解決されてサブスクリプションを確認し、プッシュボタンが有効になるまで待機します。これは、Service Worker がアクティブになって初めて、プッシュ メッセージをサブスクライブできるようになるためです。

次のステップでは、プッシュ メッセージの有効化をユーザーが希望する場合を処理しますが、その前に Google Developer Console プロジェクトを設定し、Firebase Cloud Messaging(FCM)(旧称 Google Cloud Messaging(GCM))を使用するためのパラメータをマニフェストに追加する必要があります。

Firebase Developer Console でプロジェクトを作成する

Chrome は FCM を使用して push メッセージの送信と配信を処理しますが、FCM API を使用するには Firebase Developer Console でプロジェクトを設定する必要があります。

以下の手順は、FCM を使用する Chrome、Android 版 Opera、Samsung ブラウザに固有のものです。他のブラウザでこれがどのように機能するかについては、この記事の後半で説明します。

新しい Firebase デベロッパー プロジェクトを作成する

まず、https://console.firebase.google.com/ で [新しいプロジェクトを作成] をクリックして新しいプロジェクトを作成します。

新しい Firebase プロジェクトのスクリーンショット

プロジェクト名を追加してプロジェクトを作成すると、プロジェクト ダッシュボードが表示されます。

Firebase プロジェクト ホーム

このダッシュボードで、左上にあるプロジェクト名の横にある歯車をクリックし、[プロジェクトの設定] をクリックします。

Firebase プロジェクトの設定メニュー

設定ページで [Cloud Messaging] タブをクリックします。

Firebase プロジェクトの Cloud Messaging メニュー

このページには、プッシュ メッセージング用の API キーが含まれています。このキーは後で使用します。送信者 ID は、次のセクションでウェブアプリ マニフェストに入力する必要があります。

ウェブアプリ マニフェストを追加する

push サブスクリプションを成功させるには、gcm_sender_id フィールドを含むマニフェスト ファイルを追加する必要があります。このパラメータは、Chrome、Android 版 Opera、Samsung ブラウザでのみ、FCM / GCM を使用できるようにする必要があります。

gcm_sender_id は、FCM でユーザー デバイスをサブスクライブするときにこれらのブラウザによって使用されます。つまり、FCM はユーザーのデバイスを識別し、送信者 ID が対応する API キーと一致していることと、ユーザーがサーバーからのプッシュ メッセージの送信を許可していることを確認します。

以下は、非常にシンプルなマニフェスト ファイルです。

{
    "name": "Push Demo",
    "short_name": "Push Demo",
    "icons": [{
        "src": "images/icon-192x192.png",
        "sizes": "192x192",
        "type": "image/png"
        }],
    "start_url": "/index.html?homescreen=1",
    "display": "standalone",
    "gcm_sender_id": "<Your Sender ID Here>"
}

gcm_sender_id の値を Firebase プロジェクトの送信者 ID に設定する必要があります。

マニフェスト ファイルをプロジェクトに保存したら(manifest.json が適切な名前)、ページの先頭に次のタグを付けて HTML から参照します。

<link rel="manifest" href="/manifest.json">

これらのパラメータを含むウェブ マニフェストを追加しない場合、メッセージを push するようにユーザーを登録しようとすると、"Registration failed - no sender id provided" または "Registration failed - permission denied" というエラーで例外が発生します。

プッシュ メッセージングに登録する

マニフェストの設定が完了したので、サイトの JavaScript に戻ります。

サブスクライブするには、PushManager オブジェクトで subscribe() メソッドを呼び出す必要があります。このオブジェクトには、ServiceWorkerRegistration を通じてアクセスします。

これにより、プッシュ通知を送信する権限を配信元に付与するようユーザーに求めます。この権限がないと、正常に登録できません。

subscribe() メソッドから返された Promise が解決されると、エンドポイントを含む PushSubscription オブジェクトが提供されます。

エンドポイントは、後でプッシュ メッセージを送信する際に必要になるため、ユーザーごとにサーバーに保存する必要があります。

次のコードは、プッシュ メッセージング用にユーザーをサブスクライブします。

function subscribe() {
    // Disable the button so it can't be changed while
    // we process the permission request
    var pushButton = document.querySelector('.js-push-button');
    pushButton.disabled = true;

    navigator.serviceWorker.ready.then(function(serviceWorkerRegistration) {
    serviceWorkerRegistration.pushManager.subscribe()
        .then(function(subscription) {
        // The subscription was successful
        isPushEnabled = true;
        pushButton.textContent = 'Disable Push Messages';
        pushButton.disabled = false;

        // TODO: Send the subscription.endpoint to your server
        // and save it to send a push message at a later date
        return sendSubscriptionToServer(subscription);
        })
        .catch(function(e) {
        if (Notification.permission === 'denied') {
            // The user denied the notification permission which
            // means we failed to subscribe and the user will need
            // to manually change the notification permission to
            // subscribe to push messages
            console.warn('Permission for Notifications was denied');
            pushButton.disabled = true;
        } else {
            // A problem occurred with the subscription; common reasons
            // include network errors, and lacking gcm_sender_id and/or
            // gcm_user_visible_only in the manifest.
            console.error('Unable to subscribe to push.', e);
            pushButton.disabled = false;
            pushButton.textContent = 'Enable Push Messages';
        }
        });
    });
}

この時点で、ウェブアプリは push メッセージを受信する準備が整っていますが、Service Worker ファイルに push イベント リスナーを追加するまでは何も起こりません。

Service Worker の push イベント リスナー

push メッセージを受信すると(実際に push メッセージを送信する方法については次のセクションで説明します)、Service Worker で push イベントがディスパッチされます。そこで、通知を表示する必要があります。

self.addEventListener('push', function(event) {
    console.log('Received a push message', event);

    var title = 'Yay a message.';
    var body = 'We have received a push message.';
    var icon = '/images/icon-192x192.png';
    var tag = 'simple-push-demo-notification-tag';

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

このコードは、プッシュ イベント リスナーを登録し、事前定義されたタイトル、本文、アイコン、通知タグで通知を表示します。この例で注目すべき点の一つが event.waitUntil() メソッドです。このメソッドは Promise を取り込み、Promise が解決されるまでイベント ハンドラの存続期間を延長します(つまり、Service Worker を存続したままにします)。この場合、event.waitUntil に渡される Promise は showNotification() から返される Promise です。

通知タグは、一意の通知の識別子として機能します。2 つのプッシュ メッセージを同じエンドポイントに送信し、両者の間にわずかな遅延を持たせ、同じタグを持つ通知を表示すると、ブラウザは最初の通知を表示し、プッシュ メッセージを受信すると 2 番目の通知に置き換えます。

複数の通知を一度に表示する場合は、別のタグを使用するか、タグをまったく使用しません。通知の表示のより詳細な例については、この投稿の後半で説明します。ひとまず、操作を簡素化して、プッシュ メッセージを送信するとこの通知が表示されるかどうかを確認しましょう。

push メッセージの送信

push メッセージへのサブスクライブが完了し、Service Worker で通知を表示する準備が整いました。次は、FCM を介してプッシュ メッセージを送信します。

これは、FCM を使用するブラウザにのみ適用されます。

PushSubscription.endpoint 変数をサーバーに送信する場合、FCM のエンドポイントは特別なものになります。URL の末尾に registration_id というパラメータがあります。

エンドポイントの例を次に示します。

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

FCM URL は次のとおりです。

https://fcm.googleapis.com/fcm/send

registration_id は次のようになります。

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

これは、FCM を使用するブラウザに固有のものです。通常のブラウザでは、単純にエンドポイントを取得し、そのエンドポイントを標準的な方法で呼び出すと、URL に関係なく機能します。

つまり、エンドポイントが FCM 用であるかどうかをサーバー上で確認し、そうであれば登録_id を抽出する必要があります。Python でこれを行うには 次のようにすることができます

if endpoint.startswith('https://fcm.googleapis.com/fcm/send'):
    endpointParts = endpoint.split('/')
    registrationId = endpointParts[len(endpointParts) - 1]

    endpoint = 'https://fcm.googleapis.com/fcm/send'

登録 ID を取得したら、FCM API を呼び出すことができます。FCM API のリファレンス ドキュメントをご覧ください。

FCM を呼び出すときに覚えておくべき重要な側面は次のとおりです。

  • API を呼び出すときに、値が key=&lt;YOUR_API_KEY&gt;Authorization ヘッダーを設定する必要があります。ここで、&lt;YOUR_API_KEY&gt; は Firebase プロジェクトの API キーです。
    • API キーは、FCM で適切な送信者 ID を見つけ、ユーザーからプロジェクトの権限を付与されていること、そして最後にサーバーの IP アドレスがそのプロジェクトの許可リストに登録されていることを確認するために使用されます。
  • データを JSON として送信するか、フォームデータとして送信するかに応じて、application/json または application/x-www-form-urlencoded;charset=UTF-8 の適切な Content-Type ヘッダー。
  • registration_ids の配列 - ユーザーのエンドポイントから抽出する登録 ID です。

サーバーから push メッセージを送信する方法については、ドキュメントをご覧ください。Service Worker を簡単に確認するには、cURL を使用してブラウザに push メッセージを送信します。

この cURL コマンドの &lt;YOUR_API_KEY&gt;&lt;YOUR_REGISTRATION_ID&gt; を独自のものに置き換えて、ターミナルから実行します。

次のような通知が表示されます。

    curl --header "Authorization: key=<YOUR_API_KEY>" --header
    "Content-Type: application/json" https://fcm.googleapis.com/fcm/send -d
    "{\"registration_ids\":[\"<YOUR_REGISTRATION_ID>\"]}"
Chrome for Android のプッシュ メッセージの例。

バックエンド ロジックを開発するときは、Authorization ヘッダーと POST 本文の形式が FCM エンドポイントに固有のものであることを覚えておいてください。そのため、エンドポイントが FCM の場合を検出し、条件付きでヘッダーを追加して、POST 本文をフォーマットします。その他のブラウザ(将来的には Chrome にも対応予定)では、ウェブプッシュ プロトコルを実装する必要があります。

Chrome に現在実装されている Push API の欠点は、プッシュ メッセージでデータを送信できないことです。いや、なんでもない。その理由は、今後の実装では、ペイロード データを push メッセージング エンドポイントに送信する前にサーバーで暗号化する必要があるためです。これにより、エンドポイントは、いかなる push プロバイダであっても、push メッセージの内容を簡単に表示できなくなります。これにより、HTTPS 証明書の検証が不十分な場合や、サーバーと push プロバイダ間の中間者攻撃など、他の脆弱性から保護することもできます。ただし、この暗号化はまだサポートされていないため、それまでは取得を実行して通知の入力に必要な情報を取得する必要があります。

より詳細な push イベントの例

ここまで見てきた通知はかなり基本的な内容であり、サンプルに限ると、実際のユースケースに対応するにはかなり貧弱です。

現実的には、ほとんどのユーザーは、通知を表示する前にサーバーから情報を取得したいと思うでしょう。たとえば、通知のタイトルやメッセージに特定の内容を入力したり、さらに一歩進んで一部のページやデータをキャッシュに保存して、ユーザーが通知をクリックしたときに、ネットワークが利用できない場合でも、ブラウザを開いたらすぐにすべてを利用できるようにするデータなどです。

次のコードでは、API からデータを取得し、レスポンスをオブジェクトに変換し、それを使用して通知を設定します。

self.addEventListener('push', function(event) {
    // Since there is no payload data with the first version
    // of push messages, we'll grab some data from
    // an API and use it to populate a notification
    event.waitUntil(
    fetch(SOME_API_ENDPOINT).then(function(response) {
        if (response.status !== 200) {
        // Either show a message to the user explaining the error
        // or enter a generic message and handle the
        // onnotificationclick event to direct the user to a web page
        console.log('Looks like there was a problem. Status Code: ' + response.status);
        throw new Error();
        }

        // Examine the text in the response
        return response.json().then(function(data) {
        if (data.error || !data.notification) {
            console.error('The API returned an error.', data.error);
            throw new Error();
        }

        var title = data.notification.title;
        var message = data.notification.message;
        var icon = data.notification.icon;
        var notificationTag = data.notification.tag;

        return self.registration.showNotification(title, {
            body: message,
            icon: icon,
            tag: notificationTag
        });
        });
    }).catch(function(err) {
        console.error('Unable to retrieve data', err);

        var title = 'An error occurred';
        var message = 'We were unable to get the information for this push message';
        var icon = URL_TO_DEFAULT_ICON;
        var notificationTag = 'notification-error';
        return self.registration.showNotification(title, {
            body: message,
            icon: icon,
            tag: notificationTag
        });
    })
    );
});

繰り返しになりますが、event.waitUntil() が Promise を受け取ると、結果として showNotification() によって Promise が返されます。つまり、非同期の fetch() 呼び出しが完了するまでイベント リスナーは終了せず、通知が表示されるということです。

エラーが発生している場合でも通知が表示されます。これは、設定しないと Chrome 独自の汎用通知が表示されるためです。

ユーザーが通知をクリックしたときに URL を開く

ユーザーが通知をクリックすると、Service Worker で notificationclick イベントがディスパッチされます。ハンドラ内では、タブをフォーカスする、特定の URL のウィンドウを開くなど、適切なアクションを実行できます。

self.addEventListener('notificationclick', function(event) {
    console.log('On notification click: ', event.notification.tag);
    // Android doesn't close the notification when you click on it
    // See: http://crbug.com/463146
    event.notification.close();

    // This looks to see if the current is already open and
    // focuses if it is
    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('/');
        }
    })
    );
});

この例では、同じオリジンの既存のタブがある場合はフォーカスし、そうでない場合は新しいタブを開くことで、ブラウザを開いてサイトのオリジンのルートを表示します。

Notification API でできることについては、いくつかの投稿が投稿されています。

ユーザーのデバイスの登録解除

ユーザーのデバイスを登録してプッシュ メッセージを受信しましたが、登録を解除するにはどうすればよいでしょうか。

ユーザー デバイスの登録を解除するために必要な主な作業は、PushSubscription オブジェクトの unsubscribe() メソッドを呼び出して、サーバーからエンドポイントを削除することです(これは、受信できないことがわかっているプッシュ メッセージを送信しないようにするためです)。次のコードはまさにそれを実行します。

function unsubscribe() {
    var pushButton = document.querySelector('.js-push-button');
    pushButton.disabled = true;

    navigator.serviceWorker.ready.then(function(serviceWorkerRegistration) {
    // To unsubscribe from push messaging, you need get the
    // subscription object, which you can call unsubscribe() on.
    serviceWorkerRegistration.pushManager.getSubscription().then(
        function(pushSubscription) {
        // Check we have a subscription to unsubscribe
        if (!pushSubscription) {
            // No subscription object, so set the state
            // to allow the user to subscribe to push
            isPushEnabled = false;
            pushButton.disabled = false;
            pushButton.textContent = 'Enable Push Messages';
            return;
        }

        var subscriptionId = pushSubscription.subscriptionId;
        // TODO: Make a request to your server to remove
        // the subscriptionId from your data store so you
        // don't attempt to send them push messages anymore

        // We have a subscription, so call unsubscribe on it
        pushSubscription.unsubscribe().then(function(successful) {
            pushButton.disabled = false;
            pushButton.textContent = 'Enable Push Messages';
            isPushEnabled = false;
        }).catch(function(e) {
            // We failed to unsubscribe, this can lead to
            // an unusual state, so may be best to remove
            // the users data from your data store and
            // inform the user that you have done so

            console.log('Unsubscription error: ', e);
            pushButton.disabled = false;
            pushButton.textContent = 'Enable Push Messages';
        });
        }).catch(function(e) {
        console.error('Error thrown while unsubscribing from push messaging.', e);
        });
    });
}

購読を最新の状態に保つ

FCM とサーバーの間でサブスクリプションが同期しなくなることがあります。FCM ドキュメントで説明されているように、サーバーが FCM API の送信 POST のレスポンス本文を解析し、error:NotRegisteredcanonical_id の結果を探していることを確認してください。

また、Service Worker とサーバー間でサブスクリプションが同期されない場合もあります。たとえば、サブスクライブまたはサブスクライブ解除が成功した後に、ネットワーク接続が不安定でサーバーを更新できない場合や、ユーザーが通知権限を取り消して自動登録解除をトリガーする場合があります。このようなケースに対処するには、serviceWorkerRegistration.pushManager.getSubscription() の結果を定期的に(ページ読み込み時など)チェックし、サーバーと同期します。すでにサブスクリプションがなくなり、Notification.permission == 'granted' の場合は、自動的に再度サブスクライブすることもできます。

sendSubscriptionToServer() では、endpoint を更新するときに、失敗したネットワーク リクエストをどのように処理するかを検討する必要があります。1 つの解決策は、Cookie 内の endpoint の状態を追跡して、サーバーに最新の詳細情報が必要かどうかを判断することです。

上記のすべてのステップにより、Chrome 46 でウェブにプッシュ メッセージが完全に実装されます。プッシュ メッセージをトリガーする標準 API など、作業を容易にする仕様に基づく機能はまだありますが、このリリースを使用すると、ウェブアプリへのプッシュ メッセージングの構築をすぐに開始できます。

ウェブアプリのデバッグ方法

プッシュ メッセージの実装中、バグはページか Service Worker のどちらかに存在します。

このページのバグは、DevTools を使用してデバッグできます。Service Worker の問題をデバッグするには、次の 2 つの方法があります。

  1. [chrome://inspect] > [Service Worker] に移動します。このビューでは、現在実行中の Service Worker 以外の多くの情報は表示されません。
  2. chrome://serviceworker-internals に移動すると、ここから Service Worker の状態やエラー(ある場合)を確認できます。このページは、DevTools に同様の機能セットが導入されるまで一時的なものです。

Service Worker を初めて使用する方には、[DevTools ウィンドウを開き、デバッグのために Service Worker の起動時に JavaScript の実行を一時停止します] というチェックボックスを使用するのが最もおすすめです。このチェックボックスをオンにすると、Service Worker の先頭にブレークポイントが追加され、実行を一時停止できます。これにより、Service Worker スクリプトを再開またはステップ実行して、問題が発生したかどうかを確認できます。

serviceworker-internals の [実行の一時停止] チェックボックスがある場所を示すスクリーンショット

FCM と Service Worker の push イベントの間に問題があると思われる場合は、Chrome が何かを受信したかどうかを確認する方法がないため、問題をデバッグするためにできることはほとんどありません。サーバーが API 呼び出しを行ったときに FCM からのレスポンスが成功するようにすることが重要です。次のようになります。

{"multicast_id":1234567890,"success":1,"failure":0,"canonical_ids":0,"results":[{"message_id":"0:1234567890"}]}

"success": 1 レスポンスに注目してください。エラーが表示された場合は、FCM 登録 ID に何か問題があり、プッシュ メッセージが Chrome に送信されていない可能性があります。

Chrome for Android での Service Worker のデバッグ

現時点では、Chrome for Android での Service Worker のデバッグは明らかではありません。chrome://inspect に移動してデバイスを探し、Service Worker の URL を含む「Worker pid:....」という名前のリストアイテムを探します。

Chrome の検査で Service Worker が表示される場所を示すスクリーンショット

プッシュ通知の UX

Chrome チームは、プッシュ通知の UX に関するベスト プラクティスのドキュメントと、プッシュ通知を扱う際のエッジケースをいくつか取り上げたドキュメントを作成しています。

Chrome とオープンウェブでのプッシュ メッセージの未来

このセクションでは、この実装における注意すべき Chrome 固有の部分と、他のブラウザ実装との違いについて、詳細に説明します。

ウェブプッシュ プロトコルとエンドポイント

Push API 標準の利点は、Web Push Protocol を実装することで、エンドポイントを取得してサーバーに渡して、プッシュ メッセージを送信できることです。

ウェブプッシュ プロトコルは、プッシュ プロバイダが実装できる新しい標準です。これにより、デベロッパーはプッシュ プロバイダについて心配する必要がなくなります。これにより、FCM の場合のように API キーに登録して特殊な形式のデータを送信する必要がなくなります。

Chrome は Push API を実装した最初のブラウザであり、FCM は Web Push Protocol をサポートしていないため、Chrome では gcm_sender_id が必要であり、FCM には RESTful API を使用する必要があります。

Chrome の最終目標は、Chrome と FCM でウェブプッシュ プロトコルを使用することです。

それまでは、エンドポイント「https://fcm.googleapis.com/fcm/send」を検出して、他のエンドポイントとは別に処理する必要があります。つまり、ペイロード データを特定の方法でフォーマットし、認可キーを追加する必要があります。

ウェブプッシュ プロトコルの実装方法

Firefox Nightly は現在プッシュに取り組んでおり、ウェブ プッシュ プロトコルを実装する最初のブラウザになるでしょう。

よくある質問

仕様はどこで確認できますか?

https://slightlyoff.github.io/ServiceWorker/spec/service_worker/ https://w3c.github.io/push-api/ https://notifications.spec.whatwg.org/

ウェブ プレゼンスに複数のオリジンがある場合や、ウェブ プレゼンスとネイティブ プレゼンスの両方がある場合、通知の重複を防ぐことはできますか?

現時点ではこの問題の解決方法はありませんが、Chromium で進捗状況を確認できます。

理想的なシナリオでは、ユーザー デバイス用になんらかの ID を用意し、サーバー側でネイティブ アプリとウェブアプリのサブスクリプション ID を照合して、プッシュ メッセージの送信先を決定することです。画面サイズやデバイスモデル、生成された鍵をウェブアプリとネイティブ アプリ間で共有する方法で計算できますが、それぞれの方法に長所と短所があります。

gcm_sender_id が必要な理由

これは、Chrome、Android 版 Opera、Samsung ブラウザで Firebase Cloud Messaging(FCM)API を使用するために必要です。目標は、標準が完成し、FCM がサポートできるようになった時点で、ウェブプッシュ プロトコルを使用することです。

Web Sockets や Server-Sent Events(EventSource)を使用しない理由

プッシュ メッセージを使用する利点は、ページが閉じている場合でも Service Worker が復帰し、通知を表示できることです。ページまたはブラウザが閉じると、Web Sockets と EventSource の接続は終了します。

バックグラウンド イベントの配信が不要な場合はどうすればよいですか?

バックグラウンドでの配信が不要な場合は、Web Sockets が適しています。

通知を表示せずにプッシュ機能(サイレント バックグラウンド プッシュなど)を使用できるのはどのような場合ですか?

この機能が利用可能になる時期はまだ決まっていませんが、バックグラウンド同期を実装する予定があります。また、これは決定や仕様は未定ですが、バックグラウンド同期でサイレント プッシュを有効にすることについて検討しています。

HTTPS が必要な理由開発中にこれを回避するにはどうすればよいですか?

Service Worker は、Service Worker スクリプトが意図された送信元からのものであり、中間者攻撃で発生していないことを確認するために、安全な送信元を必要とします。現時点では、ライブサイトで HTTPS を使用することになりますが、localhost は開発中にも機能します。

ブラウザ サポートの状況

Chrome は安定版でサポートしており、Mozilla は Firefox Nightly で push に取り組んでいます。詳細については、Push API の実装のバグをご覧ください。また、こちらで通知の実装を追跡できます。

一定期間が経過した通知を削除できますか?

現時点ではできませんが、現在表示されている通知のリストを取得できるようにサポートする予定です。通知の表示後に有効期限を設定するユースケースがある場合は、その旨をお知らせいただければ幸いです。コメントを追加していただければ、Chrome チームに報告いたします。

一定期間の経過後にプッシュ通知の送信を停止する必要があるが、通知の表示継続時間を気にする必要がない場合は、FCM の有効期間(ttl)パラメータを使用できます。詳細をご覧ください。

Chrome のプッシュ メッセージ機能にはどのような制限がありますか?

この投稿では、いくつかの制限事項について概説しています。

  • Chrome で CCM を push サービスとして使用すると、独自の要件が多数発生します。今後、これらの問題の一部を解除できるかどうか、Google と共同で検討しています。
  • プッシュ メッセージを受信したときに通知を表示する必要があります。
  • パソコンの Chrome には、Chrome が実行されていない場合、プッシュ メッセージは受信されないという注意点があります。これは、push メッセージが常に受信される ChromeOS や Android とは異なります。

Permissions API を使用する必要はないでしょうか?

Permission API は Chrome に実装されていますが、すべてのブラウザで使用できるとは限りません。詳しくはこちらをご覧ください

通知をクリックしても前のタブが開かないのはなぜですか?

この問題は、現在 Service Worker によって制御されていないページにのみ影響します。詳しくはこちらをご覧ください

ユーザーのデバイスがプッシュを受信した時点で通知が古くなっている場合はどうなりますか?

プッシュ メッセージを受信したときには、必ず通知を表示する必要があります。通知を送信する必要はあるものの、それが特定の期間のみ有効であるというシナリオでは、CCM で「time_to_live」パラメータを使用して、有効期限を過ぎても FCM が push メッセージを送信しないようにできます。

詳しくはこちらをご覧ください

10 件のプッシュ メッセージを送信したが、デバイスには 1 件だけ送信したい場合はどうなりますか?

FCM には「expand_key」パラメータがあります。このパラメータを使用すると、同じ「expand_key」を持つ保留中のメッセージを新しいメッセージに置き換えるように FCM に指示できます。

詳しくはこちらをご覧ください