PWA形式のWebサイトを構築してAndroidスマートフォンにインストールする

## はじめに

1つ前の記事でPWAについて簡単に解説しました。
端的にまとめれば、PWA化によるWebサービスの高速化とは、キャッシュをJS（Service Worker）によって制御して、いかに回線に頼らずに機能やコンテンツを提供するかという部分が肝になります。
今回の主題は、PWAとしてWebサイトを構築するという部分ですが、背景は「Webサービスを高速にユーザに提供できるようにする」です。
ただ、工夫された状態では学習には不向きだと思いますので、今回は最小構成でPWAとしてブラウザに認識させるというところから始めたいと思います。  
そのため、この記事ではPWAの数ある機能のうちの最も基礎となる部分を実装する方法について紹介します。
「PWAである」≒「最新のGoogle Chromeの環境でインストール可能」
という条件で作ってみたいと思います。

## 実装内容

必要となるファイルはそこまで多くありませんので、下記に全て記載します。

* index.html
* serviceWorker.js
* manifest.json
* 192✕192の.png画像ファイル

以上4点です。

削りに削ればこれだけでPWA形式としてページが表示され、Chromeがインストールしてくれます。

デモはこちらのページで確認できます。  
https://qiita-pwa.s3.ap-northeast-1.amazonaws.com/minimumPwa/index.html

それでは内容を確認しましょう。

### index.html

```html
<!DOCTYPE html>
<html>
  <head>
    <title>Hello PWA world!</title>
    <link rel="manifest" href="manifest.json">
    <script>
    if ('serviceWorker' in navigator) {
  	 navigator.serviceWorker.register('serviceWorker.js')
      .then(
      function (registration) {
          if (typeof registration.update == 'function') {
              registration.update();
          }
      })
      .catch(function (error) {
        console.log("Error Log: " + error);
      });
    }
    </script>
  </head>
  <body>
    <p>Hello, PWA world!</p>
  </body>
</html>
```

通常のindex.htmlとの違いは「マニフェストファイルへのリンク」があるのと、「Service Workerを呼ぶJS」が書いてあるだけです。

### serviceWorker.js

```javascript

var CACHE_NAME  = "MKit-cache-v1";
var urlsToCache = [
    "https://qiita-pwa.s3.ap-northeast-1.amazonaws.com/minimumPwa/",
    "https://qiita-pwa.s3.ap-northeast-1.amazonaws.com/minimumPwa/index.html"
];

self.addEventListener('install', function(event) {
    event.waitUntil(
        caches.open(CACHE_NAME)
            .then(
            function(cache){
                return cache.addAll(urlsToCache);
            })
    );
});

self.addEventListener('fetch', function(event) {
    event.respondWith(
      caches.match(event.request)
        .then(
        function (response) {
            if (response) {
                return response;
            }
            return fetch(event.request);
        })
    );
});
```

(中身無しのダミーファイルでもインストールは実行させることができるみたいです。ただ、ここすら削ってしまうと高速化の目的から外れてしまうので、ちゃんと実装します。)

Webサイトの高速化を行う上で最も重要になるのが端末のローカルストレージに格納するキャッシュファイルの取り扱いです。
キャッシュするファイルをJSによって制御するのがこちらのServeceWorker.jsの内容になります。
（プッシュ通知機能やオフライン時の動作もここへ記述します。）

こちらもキャッシュのみで、必要最低限のものに絞ればそれほど多くありません。
まず最初にくるのが、使う**キャッシュ名**です。キャッシュ名は適宜わかりやすいものに設定します。（本来はバージョン管理できるように命名しますが今回は割愛）  

次に**キャッシュの配列**です。
配列の中身は、端末にキャッシュさせたいファイルのパスをフルパス、または相対パスで指定します。
相対パスでも問題ないのですが、サンプルの段階では余計なエラーに惑わされないためにフルパスで書くことをおすすめします。
このパスにはCDNのような外部のファイルを含めることもできます。

いわずもがなですが、複数のページで使用するファイルや画像のようなサイズの大きいものをここに記述しておくと良いでしょう。

パスの設定を間違えると、jsかhtmlの一行目にエラーがあるようなコンソールのエラーメッセージが出たりしますが、ネットワークのタブを見ると、リソースがうまく読み込めていなかったりするのがわかると思います。

残りの2つの関数は、ServiceWorkerのインストールと、リソースをリクエストされたときのレスポンスの作成を行っています。
関数の詳細についてはGoogle公式のデベロッパー記事がとても参考になるので、ここのコードだけ読んで納得いかない場合はこちらを御覧ください。
https://developers.google.com/web/fundamentals/primers/service-workers/?hl=ja

### manifest.json

こちらはPWAをアプリとして起動するために必要な情報の記述を行います。

```json


{
  "name": "PWA sample for mobile kit",
  "short_name": "MKit",
  "icons": [{
        "src": "https://qiita-pwa.s3.ap-northeast-1.amazonaws.com/minimumPwa/logo192.png",
        "sizes": "192x192",
        "type": "image/png"
      }],
  "start_url": "https://qiita-pwa.s3.ap-northeast-1.amazonaws.com/minimumPwa/index.html",
  "display": "standalone",
  "background_color": "#3E4EB8",
  "theme_color": "#2F3BA2"
}
```

1つずつ解説します。

* "name": "このアプリのフルネームを記述、日本語もOKです。",
* "short_name": "このアプリのアイコンの下に付く名前を指定します。",
* "icons": [{
*       "src": "アプリのアイコンのリソースパスを書きます。",
*       "sizes": "192x192と書きます。",
*       "type": "image/pngと書きます。"
* "start_url": "指定しなくても動きますが、書いたほうが良いです。スタートページを指定します。",
* "display": "standaloneを指定すると、アドレスバーが非表示になり、アプリライクな見た目になります。",
* "background_color": "スタイルシートが読み込まれるまでの背景色を指定します。",
* "theme_color": "ツールバーの色がここで指定した色になります。"

### logo192.png

ファイル名はなんでも大丈夫です。  
192x192の正方形のpng画像ファイルであれば動くはずです。
この画像がアプリのアイコンになります。  
他の解説サイトで、144x144が必須と書いてあったような気がしますが、Windows10＋Chromeと、ZenFone2＋Chromeの環境では、192x192で動きました。  
また、iPhoneでは別のサイズといったことが考えられるため、実際にサービスとして公開する際には各環境に合わせて複数のアイコンサイズを用意しておく方が良いでしょう。

## 開発方法

### 動作確認

ファイルの準備ができたらあとはWebサーバーにアップロードするだけです。  
Apacheで動作しているサイトのHTTPSで取得できるページに適応すると、本稿の文頭でもデモとして出しましたこちらのページのようになります。

https://qiita-pwa.s3.ap-northeast-1.amazonaws.com/minimumPwa/index.html":https://qiita-pwa.s3.ap-northeast-1.amazonaws.com/minimumPwa/index.html

ここまでで、index.htmlを圧倒的速さで提供するWebサイトが完成しました。
ここで気づきましたが、高速化できたかどうかを効果測定するためには、ある程度コンテンツの量がないと難しいですよね。
そのため、実際に高速化の枠で効果検証するのは次回以降行いたいと思います。
代わりに、PWAの特徴であるオフライン動作を試してみました。

### ゲームのインストール

「リバーシアプリをJSで作ってみた」  
https://qiita-pwa.s3.ap-northeast-1.amazonaws.com/pwareversi/index.html

正月に酔っ払った勢いで行き当たりばったり実装したJSリバーシが転がっていましたので、先程の最小構成PWAのindex.htmlに突っ込みました。

Android端末（ZenFone2）でChromeを使いアクセスしたところ、無事にインストールの通知が表示され、インストールすることができました。

<img width="135" alt="addShelfZenfoneResize.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/153290/dbe06345-304f-b854-f3db-691e25de7d9d.jpeg">　<img width="135" alt="HomeLastIconInvisibleResize.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/153290/e27798e2-9725-f164-bc09-fc312fa61a19.jpeg">


ホーム画面にもアイコンが追加され、ホームから起動することができます。

動作に必要なもの全てをローカルに落とし込めているので、4G回線もWi-Fiも、全ての通信を切った状態で起動できます。

### PWAの実装時の諸注意

リバーシを実装するにあたって、jQueryを使用していたのに、キャッシュに追加し忘れて、インストールはできたのに、オフラインで動作させようとすると、（リソース不足で）オフラインのため起動できませんでしたというエラーが出ていました。
→ 何故かオフラインで起動できないというところで詰まったときは、正しくキャッシュのパスが書けているか確認しましょう。

Webサイトにアクセスしても、jsやhtmlの1行目にエラーが出て、インストールできないという事象も起こりました。
これも、パスが間違っていただけで、ディレクトリを変更したためURLが変わっており、正しくリソースが読み込めていませんでした。
→ この場合は、開発者ツールのNetworkの欄を見てみましょう。読み込めなかったリソースが赤くなっているので、そのファイルのパスを見直すことで解決できると思います。

その他に、AndroidのChromeで、メニューを表示させると、ページを保存やホーム画面へ追加という項目があると思います。
PWA形式となっている場合は、ホーム画面へ追加したあと、追加済みのサイトは、hogehogeを起動といった文言に変化します。ここでもPWAかどうか判断できます。

<img width="135" alt="MenuSupResize.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/153290/d79cdb42-cd3b-7325-be92-b4abf4162a03.jpeg">


### PCでの動作確認

Chromeの開発者ツールの中のApplicationのタブを見ると、PWAの開発に必要なものが大体揃っています。

特に今回よく使ったのが、Manifestの項目の「Add to homescreen」です。
これを押した際に、ホーム画面へ追加するメッセージが表示されれば、最低限PWA化できています。

<img width="586" alt="addhomeScreen訂正.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153290/26afa8e8-9a97-2e22-c0a9-b68b44199a0c.jpeg">

2019/06/03 現在、PC版での確認する場合は、メニューを開いたときに "「{PWA名}」をインストール"が表示されているかを見ることになります。

![pwaReversiAddHomeScreen.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153290/c7004e51-4308-17d1-67e2-61a239862600.png)


※PCでも起動できます。

「Service Worker」の項目はService Workerが正常に動いているかどうかの確認や、更新や削除ができるといったもので、その他の項目は見ればなんとなくわかるかと思います。

### おわりに

本稿では、最低限のPWAが動く構成について解説しました。
正直なところ、ここまでの内容であればPCでWebページを右クリックで名前をつけて保存したのとさほど変わりはないです。
そのため、今後は複数ページのキャッシュ、オフライン動作時にリソースが足りなかった場合のハンドリングや、上手くキャッシュをコントロールする方法について調査していきたいと思います。

続編です: https://qiita.com/OMOIKANESAN/items/13a3dde525e33eb608ae

以上