ちょっと前に作られたWebサービスをPWA化(ServiceWorkerの導入とmanifest.jsonの導入)をしたので、そちらのまとめです。同じような環境でPWAの導入を検討されている方の参考になればと思います。
(PWAとかService Workerが何者かという説明は省いております。)
対象のサービスの状態
- サーバサイド: Rails 4.1.4
- フロントエンド: jQuery
- ビルド: railsについてくるsprockets
4〜5年前ぐらいの環境だと思ってもらえれば大丈夫。
流れ
- ライブラリのインストール
- Sprocketsの読み込み設定
- デプロイ設定
ライブラリのインストール
インストールするもの
rails側
- serviceworker-rails
javascript側
- workbox-sw
- pwacompat
- babel
serviceworker-rails
railsにService Workerを導入するために必要なgem
gem 'serviceworker-rails'
javascriptファイルをsprockets管理に置いてない場合はgemでインストールしちゃうともしかしたら面倒かも...
sprockets管理のjavascriptだと /assets/hogehoge.js
という用に /assets
ディレクトリの下にファイルがくることになるかと思います。
ところがService Workerのファイルやmanifest.jsonはそれだと少々困ってしまいます。
そこで、/serviceworker.js
や /manifest.json
でアクセスした時にちゃんと /app/assets/javascripts/
以下を見るようにルーティングを調整してくれます。
URL: /serviceworker.js
↓
実ファイル: /assets/javascripts/servicewoker.js
workbox-sw
GoogleがつくっているService Workerのキャッシュ管理用ライブラリ。
今からService Workerを導入するなら黙って入れておきましょう。
npmでinstallできます。
npm install --save workbox-sw
え、npmでjsライブラリを管理していない?
そしたらこれを気に導入してしまいましょう。
下記を config/initialize/assets.rb
に追加すれば npm install
したライブラリがsprocketsで利用できるようになります。
Rails.application.config.assets.paths << Rails.root.join('node_modules')
workbox-swについて
https://developers.google.com/web/tools/workbox/
pwacompat
最近出てきた、こちらもGoogle製のライブラリ。
iOS safariはmanifest.jsonにまだ対応してないのですが(2018年7月現在)
このライブラリを使うと、iPhoneでmanifest.jsonの内容を元にスプラッシュ画面を出せるようになったり、iconを表示できるようになったり、縦横が選べるようになったり、ということができるようになります。
先ほどのworkboxとは違い、こちらはそこまで重要ではないですが入れておくと良いでしょう。
pwacompatはService Worker側でなくフロント側で読み込まれるようにする必要があります。
npm insatll --save pwacompat
pwacompat
https://github.com/GoogleChromeLabs/pwacompat
CORS対応
スプラッシュ画面に表示するアイコンの画像URLを外部サイトから読み込もうとするとCORSのエラーになってしまっていたので修正プルリク出したのですが、無事取り込まれました
babel
rails4のsprocketsにworkbox-swとか導入すると、precompile時にシンタックスエラーで無事死にます
そこでbabelでトランスパイルして、トランスパイルしたファイルをsprockets管理するようにしました。
npm install --save bable
※sprocketsのアップデートとかwebpackの導入とかも選択肢にありましたが、ハードル高そうなので一番シンプルにできるbabelにしてます。
sprocketsの読み込み設定
Service Worker側のjavascriptはgemをinstallすることで、ある程度よしなにやってくれますが、npm install
して、トランスパイルをしたライブラリは少々手を加える必要があります。
workbox-sw
-
app/assets/javascripts/serviceworker-lib.js
を新規で作成
//= require workbox-sw/build/workbox-sw-es2015
※workbox-sw-es2015はトランスパイル後のファイル名
-
config/initializers/assets.rb
に追加
Rails.configuration.assets.precompile += %w[serviceworker.js manifest.json serviceworker-lib.js]
※serviceworker.jsとmanifest.jsonは自動的に追加されてます。
- Service Worker側で読み込む
app/assets/javascripts/serviceworker.js.erb
importScripts('<%= asset_path "serviceworker-lib.js" %>') ← 追加
pwacompat
application.js
//= require hogehoge
//= require fugafuga
//= require piyopiyo
//= require pwacompat/pwacompat-es2015 ← 追加
テストについて
今回babelでトランスパイルをしたファイルを読み込むように設定をしたので、テスト実行前にbabelでトランスパイルをする必要があります。
(workboxはService Worker側で必要なファイルなのでテスト実行前のトランスパイル設定は必要ないです。)
CircleCIの場合
- run: bundle --path vendor/bundle
↓↓↓↓ この辺に追加 ↓↓↓↓
- run: node_modules/.bin/babel node_modules/pwacompat/pwacompat.js -o node_modules/pwacompat/pwacompat-es2015.js
- run:
name: Setup database
command: bundle exec rake db:create db:schema:load
Androidシミュレータ
開発環境でAndroidでPWAが正しく動作をしているか確認するようにAndroidシミュレータをインストールします。
今回はGenymotionというシミュレータを使用。
他サイトのリンクですが、こちらを参考に設定しました
https://hep.eiz.jp/genymotion-google-play/
デプロイ
railsでおなじみの capistrano
に、今回babelを使ってトランスパイルするので、それを追加しておきます。
追加する場所が少々厄介でした。
実行したいタスクの順番は下記の通り
タスクの順番
- npm install
- babel
- precompile
npm install
の後で、precompile
の前に実行をしたいのですが、
capistorano-npm
を使うとこの間にタスクを追加することができませんでした
なので仕方なく自前で npm install
のタスクを追加して babelコマンドを実行します。
task :npm_install do
on roles( %w(<precompile 実行対象サーバーのrole>) ) do
within release_path do
with rails_env: fetch(:rails_env) do
execute "npm install --prefix #{release_path}"
end
end
end
end
desc 'execute babel'
task :babel do
on roles( %w(<precompile 実行対象サーバーのrole>) ) do
within release_path do
with rails_env: fetch(:rails_env) do
execute "#{release_path}/node_modules/.bin/babel #{release_path}/node_modules/workbox-sw/build/workbox-sw.js -o #{release_path}/node_modules/workbox-sw/build/workbox-sw-es2015.js"
execute "#{release_path}/node_modules/.bin/babel #{release_path}/node_modules/pwacompat/pwacompat.js -o #{release_path}/node_modules/pwacompat/pwacompat-es2015.js"
end
end
end
end
before :compile_assets, :npm_install
after :npm_install, :babel
task :babel
がbabelを実行しているタスクです。
within release_path do 〜 end
の中で 実行している execute
の後がbableコマンドです。
execute "#{release_path}/node_modules/.bin/babel #{release_path}/node_modules/workbox-sw/build/workbox-sw.js -o #{release_path}/node_modules/workbox-sw/build/workbox-sw-es2015.js"
execute "#{release_path}/node_modules/.bin/babel #{release_path}/node_modules/pwacompat/pwacompat.js -o #{release_path}/node_modules/pwacompat/pwacompat-es2015.js"
release_pathを指定しているのがおかしな感じですが、なぜかパスを指定しないとうまく動いてくれなかったのでそうしてます。
なくても動くかも。