igataをPWA化する３

# はじめに

[最初の記事](https://qiita.com/ufoo68/items/83e877d3b901b513c073)でigataにserviceWorkerを入れ込んだと思い込んでいたら**正常に動いてないことがわかりました**（普通は確かめるものですが、@ufoo68は重度の面倒臭がりなので記事を書いた後で調べてわかりました）。どうやって確かめたかというと（やり方は色々ありますが）私は、

- [PWA builder](https://www.pwabuilder.com/)を使う
- [Lighthouse PWA Analysis Tool](https://developers.google.com/web/ilt/pwa/lighthouse-pwa-analysis-tool)を使う

の2つをやってみました。それぞれの結果をみて、**明らかにServiceWorkerがちゃんと登録されていないことがわかりました。**今回はそれを改善したのでそれについてまとめます（つまりは進捗プラマイゼロ）

# やったこと

当初はReact側の親元のファイルである`main.tsx`にServiceWorkerを呼び出すようなスクリプトを書いていましたが、それがもしかしたら相性が悪いのかなと思い（実際コンソールでよくわからないエラーを吐いていた）、`webpack`の方でどうにかならないかなと調べてみました。すると2つのいい感じの記事が見つかりました。

- [Workbox + webpackでServiceWorkerのオフラインキャッシュと戯れる](https://www.hivelocity.co.jp/blog/46013/)
- [workbox + webpackでServiceWorkerを使いPWA化してみた](https://qiita.com/aaaaayako/items/a4a19dba0b4c9e4b396a)

# webpackでServiceWorkerを楽に登録する

調べてみる感じ、`workbox-sw`と`workbox-webpack-plugin`を使えばwebpackでServiceWorker関係のコードを生成してくれるみたいな気がします。早速`yarn add`しました。

```
yarn add -D workbox-sw workbox-webpack-plugin
```

あとは元のigataの`webpack.common.js`の`plugins`を拡張します。

```js:webpack.common.js
const plugins = [
  new HtmlWebpackPlugin({
    favicon: param.faviconPath,
    templateParameters: { title: param.title },
    template: param.templatePath,
  }),
  new MiniCssExtractPlugin({
    filename: '[name].css',
    chunkFilename: '[id].css',
  }),
  new WorkboxWebpackPlugin.GenerateSW({
    swDest: 'sw.js',
    clientsClaim: true,
    skipWaiting: true,
  }),
]
```

この`WorkboxWebpackPlugin.GenerateSW`が`sw.js`がServiceWorkerのスクリプトを生成してくれます。今回は最小限の設定のみ行いました。
あとはこの`sw.js`を登録してもらうように`src/assets/html/template.html`を拡張します。

```html:src/assets/html/template.html
<script>
  if ('serviceWorker' in navigator) {
    navigator.serviceWorker
      .register('sw.js')
      .then(function(registration) {
        console.log('ServiceWorker registration successful with scope: ', registration.scope)
      })
      .catch(function(err) {
        console.log('ServiceWorker registration failed: ', err)
      })
  }
</script>
```

スクリプトの内容は先ほどの記事から拝借しました。React的にはHTMLにJavaScript書くのはよろしくないのかもしれませんが、これは単純に`sw.js`を呼び出すだけの単純なものなので良しとしましょう（そもそも別スレッドで動くものだし。。という言い訳もしてみる）。

あとはついでに`manifest.webmanifest`を登録してないともPWA bulderに怒られたのでこれも追加することにしました。やったこととしては`src/assets/manifest`というフォルダを新しく作って、そこにマニフェストファイルを作成しました。

```js:src/assets/manifest/manifest.webmanifest
{
	"dir" : "ltr",
	"lang" : "en",
	"name" : "igata",
	"scope" : "/",
	"display" : "browser",
	"start_url" : "https://igata-pwa.firebaseapp.com/",
	"short_name" : "igata",
	"theme_color" : "transparent",
	"description" : "",
	"orientation" : "any",
	"background_color" : "transparent",
	"related_applications" : [],
	"prefer_related_applications" : false,
	"icons" : [
		{
			"src": "https://igata-pwa.firebaseapp.com/favicon.ico",
			"sizes": "16x16 24x24 32x32 64x64"
		}],
	"url" : "https://igata-pwa.firebaseapp.com",
	"generated" : "true"
}
```

このファイルの内容はPWA bulderが勝手に自動生成してくれました。あとは先ほどの`template.html`に以下を追加します。

```html:src/assets/html/template.html
<link rel="manifest" href="/manifest.webmanifest" />
```

この`manifest.webmanifest`をwebpackの出力先にちゃんと出力してくれるようにします。今回は`copy-webpack-plugin`を使いました。

```
yarn add -D copy-webpack-plugin
```

[ここ](https://vaadin.com/learn/tutorials/learn-pwa/production-pwa-webpack-setup)を読んで見つけました。とりあえずまたigataの`webpack.common.js`の`plugins`を拡張します。

```js:webpack.common.js
const plugins = [
  new HtmlWebpackPlugin({
    favicon: param.faviconPath,
    templateParameters: { title: param.title },
    template: param.templatePath,
  }),
  new MiniCssExtractPlugin({
    filename: '[name].css',
    chunkFilename: '[id].css',
  }),
  new WorkboxWebpackPlugin.GenerateSW({
    swDest: 'sw.js',
    clientsClaim: true,
    skipWaiting: true,
  }),
  new CopyWebpackPlugin(['src/assets/manifest/manifest.webmanifest']),
]
```

あとは、これで`manifest.webmanifest`もちゃんと出力してくれます。

# 結果

とりあえず一部をのぞいてPWAの対応ができたっぽいです。

<img width="1493" alt="スクリーンショット 2020-02-06 9.51.10.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209689/22b34509-15e0-5157-619c-a1eea0a7ce5d.png">

残るは**プッシュ通知**ですね。

# さいごに

プッシュ通知はまたどこかで対応するとして、次からは本格的にアプリを作っていきたいです。