OpenWeb でのプッシュ通知

デベロッパーを集めてモバイル端末の機能にあってウェブにはないものは何かと質問すると、プッシュ通知は常に上位にあげられます。

プッシュ通知を使用すると、ユーザーは、お気に入りのサイトから最新情報を得られるようになります。一方、デベロッパー側は、関連性のあるコンテンツをカスタマイズして、効率的にユーザーのリピート率を高めることができます。

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

Chrome の Push API は、ウェブアプリ マニフェストService Worker など、いくつかの異なるテクノロジーに依存しています。この投稿では、これらのテクノロジーのそれぞれについて説明しますが、プッシュ メッセージを運用するために必要な最小限のもののみを説明します。マニフェストのその他の機能や、サービス ワーカーのオフライン機能について詳しくは、上記のリンクをご覧ください。

また、今後の Chrome のバージョンで API に追加される機能についても説明します。最後に、よくある質問を紹介します。

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

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

Service Worker の登録

ウェブのプッシュ メッセージを実装するには、Service Worker が必要です。これは、プッシュ メッセージを受信すると、ブラウザが Service Worker を起動できるためです。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 は、プッシュ メッセージが現在登録されているかどうかを単に追跡するグローバル変数です。これらはコード スニペット全体で参照されます。

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

初期状態を設定する

Chrome でプッシュ メッセージの有効化と無効化を切り替えた場合の UX の例。

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

ユーザーは、サイトのプッシュ メッセージを有効または無効にするためのシンプルな UI を期待しています。また、変更が発生した場合は、その UI が最新の状態に保たれることを期待しています。つまり、ユーザーがサイトのプッシュ メッセージを有効にして、1 週間後にサイトに戻ってきた場合、プッシュ メッセージがすでに有効になっていることが UI にハイライト表示されます。

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

この時点では、有効または無効の 2 つの状態しか扱えないと思われるかもしれません。ただし、通知に関するその他の状態も考慮する必要があります。

Chrome でのプッシュのさまざまな考慮事項とステータスを示す図

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

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

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

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

// 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);
        });
    });
}

これらの手順の概要は次のとおりです。

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

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

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

Firebase デベロッパー コンソールでプロジェクトを作成する

Chrome は FCM を使用してプッシュ メッセージの送信と配信を処理しますが、FCM API を使用するには、Firebase デベロッパー コンソールでプロジェクトを設定する必要があります。

以下の手順は、FCM を使用する Chrome、Android 版 Opera、Samsung ブラウザに固有の手順です。他のブラウザでの動作については、この記事の後半で説明します。

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

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

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

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

Firebase プロジェクトのホーム

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

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

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

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

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

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

プッシュの場合は、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">

これらのパラメータを含むウェブ マニフェストを追加しないと、ユーザーをプッシュ メッセージに登録しようとしたときに、エラー "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';
        }
        });
    });
}

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

Service Worker プッシュ イベント リスナー

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

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 番目の通知に置き換えます。

複数の通知を一度に表示する場合は、別のタグを使用するか、タグを使用しないようにします。通知を表示するより完全な例については、この投稿の後半で説明します。現時点では、シンプルにするため、プッシュ メッセージを送信してこの通知が表示されるかどうかを確認します。

プッシュ メッセージの送信

プッシュ メッセージをサブスクライブし、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 用かどうかを確認し、FCM 用の場合は registration_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 として送信するかフォームデータとして送信するかに応じて、適切な Content-Type ヘッダー(application/json または application/x-www-form-urlencoded;charset=UTF-8)。
  • registration_ids の配列 - ユーザーのエンドポイントから抽出された登録 ID です。

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

この 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>\"]}"
Android 版 Chrome からのプッシュ メッセージの例。

バックエンド ロジックを開発する際は、Authorization ヘッダーと POST 本文の形式が FCM エンドポイントに固有であることを念頭に置いてください。エンドポイントが FCM 用かどうかを検出し、条件付きでヘッダーを追加して POST 本文をフォーマットします。他のブラウザ(将来的には Chrome も)では、ウェブプッシュ プロトコルを実装する必要があります。

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

より完全なプッシュ イベントの例

ここまで見てきた通知は非常に基本的なものです。サンプルとしては、実際のユースケースをカバーするには不十分です。

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

次のコードでは、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 の send 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 workers に移動します。このビューには、現在実行中の Service Worker 以外の情報はほとんど表示されません。
  2. chrome://serviceworker-internals にアクセスすると、Service Worker の状態とエラー(ある場合)を確認できます。このページは、DevTools に同様の機能セットが追加されるまでの一時的なものです。

Service Worker を初めて使用する方には、[Open DevTools window and pause JavaScript execution on service worker startup for debugging] というチェックボックスを使用することをおすすめします。このチェックボックスをオンにすると、Service Worker の開始位置にブレークポイントが追加され、実行が一時停止します。これにより、Service Worker スクリプトを再開またはステップ実行して、問題が発生していないか確認できます。

serviceworker-internals で実行の一時停止チェックボックスが表示されている場所を示すスクリーンショット。

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

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

"success": 1 レスポンスに注目してください。失敗した場合は、FCM 登録 ID に問題があり、プッシュ メッセージが Chrome に送信されていないことを示しています。

Android 版 Chrome で Service Worker をデバッグする

現時点では、Android 版 Chrome でサービス ワーカーをデバッグするのは簡単ではありません。chrome://inspect に移動し、デバイスを見つけて、Service Worker の URL を含む「Worker pid:....」という名前のリストアイテムを探します。

Chrome Inspect でサービス ワーカーが存在する場所を示すスクリーンショット

プッシュ通知の UX

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

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

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

ウェブ push プロトコルとエンドポイント

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

Web Push Protocol は、プッシュ プロバイダが実装できる新しい標準であり、デベロッパーはプッシュ プロバイダを気にする必要がなくなります。これにより、FCM で必要となる API キーの登録や、特別な形式のデータの送信が不要になります。

Chrome は Push API を実装した最初のブラウザであり、FCM はウェブ プッシュ プロトコルをサポートしていません。そのため、Chrome では gcm_sender_id が必要になり、FCM には RESTful API を使用する必要があります。

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

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

ウェブ push プロトコルを実装する方法

Firefox Nightly は現在プッシュに取り組んでおり、Web Push プロトコルを最初に実装するブラウザになる可能性があります。

よくある質問

仕様はどこにありますか?

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 Push プロトコルを使用することです。

ウェブ ソケットやサーバー送信イベント(EventSource)を使用しないのはなぜですか?

プッシュ メッセージを使用するメリットは、ページが閉じている場合でも Service Worker が起動され、通知を表示できることです。WebSocket と EventSource の接続は、ページまたはブラウザが閉じられると切断されます。

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

バックグラウンド配信が不要な場合は、Web ソケットが最適です。

通知を表示せずにプッシュを使用できる場合(サイレント バックグラウンド プッシュ)

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

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

Service Worker では、Service Worker スクリプトが意図したオリジンからのものであり、中間攻撃から発生したものではないことを確認するために、安全なオリジンが必要です。現在のところ、これは本番環境で HTTPS を使用することを意味します。ただし、開発中はローカルホストを使用できます。

ブラウザのサポート内容

Chrome は安定版でサポートしており、Mozilla は Firefox Nightly でプッシュに取り組んでいます。詳しくは、Push API の実装のバグをご覧ください。通知の実装の進捗状況のトラッキングはこちらです。

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

現時点では、この操作はできませんが、現在表示されている通知のリストを取得するサポートを追加する予定です。通知の作成後に通知の有効期限を設定するというユースケースがある場合は、コメントを追加してください。Chrome チームに伝えます。

プッシュ通知がユーザーに送信されるのを特定の期間後に停止するだけで、通知が表示される期間は気にしない場合は、FCM の有効期間(TTL)パラメータを使用できます。詳細

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

以下の制限事項にご注意ください。

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

Permissions API を使用するべきではないですか?

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

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

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

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

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

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

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

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

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