serviceworkerでwebpush!

serviceworkerでwebpush!

はじめに

プッシュ通知といえばスマホにアプリのインストールして初めて受け取れる(配信できる)もの、というイメージがありますが、
webpushを使うことでアプリをインストールすることなく、通常のPCやスマホのwebブラウザに向けてプッシュ通知を配信することが出来ます。

このwebpushとは独自技術ではなくIETFによって策定され各ブラウザによって実装された、もしくは将来的にされる仕様です。
つまりブラウザさえインストールされていればプッシュ通知を配信することが出来るので、ネイティブアプリによるプッシュ通知と同等以上のリテンションが期待できる可能性があると思います。

webpush

webpushを受け取るためにはservice workerが必要になります。service workerとはwebページとは別にブラウザがバックグラウンドで実行するjavascriptで、簡単に言えばネイティブアプリのような機能をwebでも提供する技術です。詳しくは以下を御覧ください。
https://developers.google.com/web/fundamentals/getting-started/primers/service-workers?hl=ja

重要な点としてservice workerはlocalhostかhttpsのプロトコルでのみ動作します。そのためwebpushもそのどちらかでしか動作しません。

webpushは通常のiOS、androidアプリと同じようにプッシュ配信サーバ(Firebase等)を通して配信されます。詳しい仕組みはこのあたりの記事で。
http://qiita.com/tomoyukilabs/items/217915676603fda73b0a

対応ブラウザはChrome、Firefox、Safari(OS X)です。

実装

仕様等は置いといてRailsでの実装をしてみます。
サーバ側から任意のタイミングでプッシュ通知を送りたいため、ブラウザ毎のプッシュ配信用の情報をDBに保存しておき、特定ユーザに向けてプッシュを送れるようにしてみたいと思います。

DB構成

簡略化のため以下を想定します。

users
Field Type Null Key Default Extra
id int(11) NO PRI NULL auto_increment
devices
Field Type Null Key Default Extra
id int(11) NO PRI NULL auto_increment
user_id int(11) NO MUL NULL
endpoint varchar(255) NO NULL
p256dh varchar(255) NO NULL
auth varchar(255) NO NULL

gem

実装にあたっては以下のgemを使用します。
https://github.com/zaru/webpush
https://github.com/rossta/serviceworker-rails

VAPIDの生成

インストールしたらプッシュ配信用にpublic_keyprivate_keyを生成します。
VAPIDについては以下。
http://qiita.com/tomoyukilabs/items/9346eb44b5a48b294762#%E5%85%A8%E4%BD%93%E3%81%AE%E6%B5%81%E3%82%8C

生成したpublic_keyprivate_keydotenv-rails等で環境変数に設定しておきます。

manifest.jsonの宣言

次にmanifest.jsonを宣言します。詳細な仕様は以下。
https://developer.mozilla.org/en-US/docs/Web/Manifest

上記のmanifest.jsonをhtmlのhead内で読み込みます。

service workerの登録

service workerは一つのドメインに対してパス毎に複数登録することができます。それぞれのservice workerの影響範囲のスコープは登録パスより下のパスに限定されるため、例えば/contents/:idへのスコープを持ちたければ少なくとも/contentsにインストールする必要があります。今回は/に一つだけインストールします。

railsのasset pipelineとservice workerの共存

railsのasset pipelineではプリコンパイルされたファイルは/assets/以下に配置されますが、このままだとservice workerの上記仕様によりスコープは/assets/以下になってしまいます。この問題を解決するためにserviceworker-railsgemを使用します。

rails g serviceworker:install

以下のファイルが作成されます。
config/initializers/serviceworker.rb – for configuring your Rails app
app/assets/javascripts/serviceworker.js.erb – a blank Service Worker script with some example strategies
app/assets/javascripts/serviceworker-companion.js – a snippet of JavaScript necessary to register your Service Worker in the browser
app/assets/javascripts/manifest.json.erb – a starter web app manifest pointing to some default app icons provided by the gem
public/offline.html – a starter offline page

また、以下のファイルが変更されます。
– Adds a sprockets directive to application.js to require serviceworker-companion.js
– Adds serviceworker.js and manifest.json to the list of compiled assets in config/initializers/assets.rb
– Injects tags into the head of app/views/layouts/application.html.erb for linking to the web app manifest

service workerregister用コードを記述していきます。

vapidpublic_keyBase64でデコード。

上記文字列をjavascriptに渡す。

service workerの登録とプッシュ通知の購読。

これでプッシュ通知の購読が成功すれば/devicesにプッシュ配信用のパラメータがPOSTされます。
このパラメータを保存します。

ちなみにモデルは以下のようにしています。

User

Device

リソースの定義。

コントローラの定義。受け取ったパラメータに変更があれば追加で保存します。ユーザ認証にDeviseを用いています。

プッシュの配信(サーバ側実装)

サーバ側からプッシュの配信リクエストを送ります。色々なところから呼び出す可能性があるのでサービスクラスに実装します。

service workerスクリプトの実装

service workerの中身を実装します。今回はwebpushが目的なのでキャッシュ等は考慮しません。

完成

以上で一通り実装できました。rails sして画面を叩くとservice workerが登録されるタイミングで以下のようなSQLが発行されてendpoint等のパラメータが登録されます。

SQL (0.4ms)  INSERT INTO `devices` (`user_id`, `endpoint`, `p256dh`, `auth`, `created_at`, `updated_at`) VALUES (1, 'https://fcm.googleapis.com/fcm/send/dliOR...:APA91bEhd...', 'BFJBsocE...', 'gDmTL2...', '2017-05-08 10:49:57', '2017-05-08 10:49:57')

この状態でrails cでコンソールを起動し以下のコマンドを叩くとwebpushが来てくれるはずです。

WebpushService.new.webpush_clients('hoge')

スクリーンショット 2017-05-08 19.51.37

終わりに

というわけでwebpushを実装してみました。
なんだか長くなってしまいましたが、実際にプッシュ通知を送信する部分と購読する部分の処理は結構簡単なので、重要な情報や非同期な処理の完了などを通知してあげるとシャレオツなんじゃないかと思います。

参考

https://developer.mozilla.org/ja/docs/Web/API/WindowClient
https://tech.drecom.co.jp/introduction-of-the-web-push-notification-system-using-the-service-worker/
https://developer.mozilla.org/en-US/docs/Web/Events/notificationclick
http://qiita.com/tomoyukilabs/items/217915676603fda73b0a
https://rossta.net/blog/using-the-web-push-api-with-vapid.html
http://qiita.com/tomoyukilabs/items/9346eb44b5a48b294762

TAG

  • このエントリーをはてなブックマークに追加
kurashita
エンジニア kurashita kurashita

基本的にRuby on Railsで開発してます。最近はvue.jsも。好きな塔は円城です。