OpenWeb でのプッシュ通知

Matt Gaunt 氏

デベロッパーの部屋に、ウェブにないモバイル デバイス機能について質問すると、プッシュ通知が常に上位に表示されます。

プッシュ通知を使用すると、ユーザーはお気に入りのサイトの最新情報をタイムリーに受け取ることができます。また、デベロッパーはカスタマイズされた魅力的なコンテンツで、ユーザーに効率的にアプローチできます。

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

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

また Chrome の今後のバージョンで API に何が追加されるのかについても検討し 最後によくある質問を紹介します

Chrome のプッシュ メッセージングを実装する

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

Service Worker を登録する

ウェブ用のプッシュ メッセージを実装するには、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.js ファイルを登録する前に、Service Worker がサポートされているかどうかを確認します。ここでは、この JavaScript ファイルがサイトの Service Worker であることをブラウザに伝えるだけです。

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

Chrome のプッシュ メッセージ機能の有効化または無効化の例。

Service Worker が登録されたら、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 が使用可能であることを確認します。これがないと、プッシュ メッセージの受信時に Service Worker からの通知を表示できません。
  • 現在の Notification.permission が何であるかを確認し、"denied" でないことを確認します。権限が拒否された場合、ユーザーがブラウザで手動で権限を変更するまで通知を表示できません。
  • プッシュ メッセージングがサポートされているかどうかを確認するには、window オブジェクトで PushManager が使用可能であることを確認します。
  • 最後に、pushManager.getSubscription() を使用して、すでに定期購入があるかどうかを確認しました。その場合、正しい情報を確保するために定期購入の詳細をサーバーに送信し、プッシュ メッセージがすでに有効になっているかどうかを示す UI を設定します。購読オブジェクトの詳細についてはこの記事の後半で説明します。

navigator.serviceWorker.ready が解決されてサブスクリプションの確認とプッシュボタンの有効化が行われるのを待ちます。これは、Service Worker がアクティブになってから初めて、プッシュ メッセージを実際にサブスクライブできるためです。

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

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

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

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

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

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

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

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

Firebase プロジェクト ホーム

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

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

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

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

このページには、プッシュ メッセージの API キー(後で使用します)と、送信者 ID が記載されています。送信者 ID は次のセクションでウェブアプリ マニフェストに入力します。

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

push の場合、push サブスクリプションを正常に完了するには、gcm_sender_id フィールドを含むマニフェスト ファイルを追加する必要があります。このパラメータは、Chrome、Opera for Android、Samsung Browser でのみ、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 から次のタグをページの head に挿入して参照します。

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

これらのパラメータを使用してウェブ マニフェストを追加しないと、メッセージを push するためにユーザーをサブスクライブしようとしたときに例外が発生し、"Registration failed - no sender id provided" または "Registration failed - permission denied" エラーが発生します。

Push メッセージングに登録する

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

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

これにより、プッシュ通知を送信する権限をオリジンに付与するようユーザーに求められます。この権限がないと、サブスクライブできません。

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

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

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

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 ファイルに push イベント リスナーを追加するまで何も起こりません。

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

プッシュ メッセージを受信すると(プッシュ メッセージを実際に送信する方法については、次のセクションで説明します)、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 が解決されるまで、イベント ハンドラの存続期間を延長します(または、サービス ワーカーの存続期間とみなすことができます)。この場合、event.waitUntil に渡される Promise は、showNotification() から返される Promise です。

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

複数の通知を一度に表示する場合は、別のタグを使用するか、タグを使用しないようにします。この記事の後半では、通知を表示するより詳細な例を紹介します。今はシンプルに、プッシュ メッセージを送信するとこの通知が表示されるかどうかを確認しましょう。

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 用であるかどうかをサーバー上で確認し、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 を呼び出すときに、Authorization ヘッダーに key=&lt;YOUR_API_KEY&gt; を設定する必要があります。ここで、&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 です。

サーバーからプッシュ メッセージを送信する方法については、こちらのドキュメントをご覧ください。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>\"]}"
Chrome for Android のプッシュ メッセージの例。

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

Chrome に Push API が現在実装されている場合、プッシュ メッセージでデータを送信できないという欠点があります。いや、なんでもない。これは、今後の実装では、ペイロード データを push メッセージング エンドポイントに送信する前にサーバーで暗号化する必要があるためです。これにより、エンドポイントは、それがプッシュ プロバイダであっても、プッシュ メッセージの内容を簡単には表示できなくなります。これにより、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 API の送信 POST のレスポンス本文を解析し、error:NotRegisteredcanonical_id の結果を探します(FCM のドキュメントを参照)。

また、サブスクリプションが 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 を初めて使うすべての人に役立つヒントの一つは、[Open 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 になんらかの問題があり、push メッセージが 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 標準の利点は、ウェブプッシュ プロトコルを実装することで、エンドポイントを取得してサーバーに渡し、プッシュ メッセージを送信できることです。

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

Chrome は Push API を実装した最初のブラウザです。FCM ではウェブプッシュ プロトコルがサポートされていないため、Chrome では gcm_sender_id が要求され、FCM には REST の 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、Opera for Android、Samsung ブラウザで Firebase Cloud Messaging(FCM)API を使用できるようにするために必要です。目標は、標準が確定し、FCM でサポートできるようになったらウェブプッシュ プロトコルを使用することです。

WebSocket や Server-Sent Events(EventSource)を使用しないのか。

プッシュ メッセージを使用するメリットは、ページが閉じられても、Service Worker が起動して通知を表示できるようになることです。Web Sockets と EventSource は、ページまたはブラウザが閉じると接続が閉じられます。

バックグラウンド イベントの配信が不要な場合

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

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

いつ利用できるかはまだ決まっていませんが、バックグラウンド同期を実装する意向があり、決定や仕様は決まっていませんが、バックグラウンド同期によるサイレント プッシュの有効化については検討中です。

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

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

ブラウザ サポートはどのようになっていますか?

Chrome は安定版でサポートされ、Mozilla は Firefox Nightly でプッシュに対応しています。 詳しくは、Push API の実装のバグをご覧ください。また、プッシュ通知の実装を追跡できます。

一定期間経過後に通知を削除できますか?

現時点ではできませんが、現在表示されている通知のリストを取得するサポートを追加する予定です。通知の作成後に有効期限を設定するユースケースがある場合は、ぜひお知らせください。いただいた情報は Chrome チームに報告します。

一定期間の経過後にプッシュ通知をユーザーに送信しないようにのみする必要があり、通知の表示期間には問題がない場合は、FCM の有効期間(ttl)パラメータを使用できます。詳細

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

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

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

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

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

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

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

ユーザーのデバイスがプッシュを受け取る頃には通知が古くなっている場合はどうなりますか?

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

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

プッシュ メッセージを 10 回送信し、1 回しか受信したくない場合はどうなりますか?

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

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