PWAは便利ですがその一方で正直分かりにくいというデメリットもあります。
Vueや、私の利用しているQuasarなどはPWAモードが予め用意されているので、プロジェクト作成時にPWAの指定をするだけで難しい処理もなく、簡単にPWAとしてアプリをビルドすることができます。
非常にありがたい一方で細かい制御のやり方が本当に分かりにくい。そしてデバッグもしにくいという罠もあります。
今回はPWAでアプリの更新が来たときに、ユーザに通知を促してアプリの更新を行ってもらう処理を埋め込むまでの紆余曲折についてまとめました。
主にQuasarを基準としていますが、純粋なVueでもほぼ同様のことが言えると思います。
Register Service Worker#
PWAとしてQuasarをビルドすると、プロジェクト直下にsrc-pwaというディレクトリが作成され、その中に「register-service-worker.js(ts)」というファイルが自動で作成されます。
中身はこんな感じでシンプルです。
Service Workerのそれぞれのイベントに応じて、行いたい処理を記述してあげるという感じでしょうか。
今回はアプリの更新が完了したら、ユーザにその旨を通知し、アプリを更新してもらいたいわけです。よって updatedの部分に何かしらの処理を書けば良いわけです。
Quasar公式サイトの真似をしてみる#
実はQuasarの公式Webサイトも更新通知機能が実装されており、最初はこれを真似すればよいのでは?と安易に考えていました。
Quasarの公式サイト自体がGithubに公開されているため、まずはGithubの中身を確認してみます。そのファイルはここから見れます
リンク先と同じコードをここにも書いておきます。ファイル名は register-service-worker.js です。(.tsではありませんでした)
これとは別に、quasar.conf.jsのファイルも一部書き換えが必要です。更新が発生したときに処理を待たずに更新するような設定にします。
quasar.conf.jsはQuasar独自の設定ファイルです。他のフレームワークだと別のファイルになると思います
これで準備は完了。何度かのテストを行い、問題なくメッセージが表示され更新もうまくいきました。
iPadやAndroidで更新時にアプリがフリーズするトラブルが発覚#
PCのブラウザであれば問題なく更新ができていたPWAですが、AndroidやiPadでPWAの更新がうまくできないことがわかりました。
こちらの手順でPWAをタブレットにインストールし、更新をテストしたところ、更新のメッセージが表示され、「後で更新」を選ぶとvue Routerが機能しなくなり、ページ遷移ができなくなります。
余談ですがiPhoneでは正常に更新ができます。(それにより発見が遅れたため悪いケースです)
色々調べてみたところ、Service Worker内ではwindowにアクセスできないようです。ただregister-service-workerを使うと色々アクセス出来るようですし、実際にPCやiPhoneでは正常に更新ができていたため、iPadOSなど特定のOSで対応していないのかもしれません。
正直原因はよくわかりませんが、問題を解決するほうが重要です。
Service worker上ではなくVue上で更新処理を行うように修正#
service workerについて調べると、とにかくよく出てくるコード。
どうやらserviceWorkerを使うにはこのような処理を書く必要があるのだと思います。しかしregister-service-workerがこの辺を良しなに自動でやってくれているはずです。
だからこそPC版では正常に更新ができているのです。自分でこの処理を書いたら2回登録されることになってしまうのではないか?といろいろ調べて回った結果、ついに答えとなるWebサイトを発見しました。
このWebサイトこそ救世主でした
正解を先に書くと、この記事の章タイトルの通りで、「Register Servie worker上ではなくVue上で更新処理を行う」ことで、iPadなどの機器でも正常に更新が行えるようになりました。
以下、修正の具体的な例です。参考にさせていただいたサイトはVuetifyですが、私はQuasarを使っているのでQuasar基準の解答です。
register-service-worker.jsで更新イベントを発行する#
まずはregister-service-worker.js側の修正です
これでService workerがUpdateになったとき、swUpdatedというイベントが発行されます。このイベントをVue側で受け取り処理を引き継ぐ感じですね
App.vue側でswUpdateイベントを広い、更新通知を行う#
Quasarのプロジェクトでは、プロジェクトフォルダ/srcの中にApp.vueというファイルがあります。
今回はこのApp.vue上でイベントを拾ってみます。
App.vueは最もルートのコンポーネントなので初期化や今回のようなイベントリッスンには最適です
App.vueは次のようになりました。短いので全文掲載します。
provideとかは今回の話題とは関係ないのでスルーしてください。ポイントは以下のコードです。
register-service-worker.jsで発行した「swUpdated」イベントを、App.vue側で受け取ります。引数としてカスタムイベントも受け取れますが、私の場合は特に使用しませんでした。
さてこのカスタムイベントを受け取ったら更新準備が完了したということなので、あとは簡単です。
更新通知を発行してあげて、ユーザに「今すぐ更新か?あとで更新か?」を選んでもらいます。
私はQuasarが大好きなので、上のコードもQuasar独自の命令が含まれています。そう、通知をトーストで表示する「Notify」です。
他のフレームワークをお使いの方は、この箇所をそれぞれの通知命令に書き換えてください。
WebpackのOptionsでskipwaitingを無効化しておくことを忘れずに#
私の利用しているQuasarではWebpackの設定をquasar.conf.jsで記述します。
ここでskipWaitingを無効にしておく必要があるので注意。これが有効だと更新通知のボタンを押そうが押すまいが、Skip-waitingが発動してしまい、VueRouterがうまく機能しなくなります。