PWA（Progressive Web Apps）を最低限で試してみる

#概要
PWAアプリを広く浅く最低限で試してみた記録です。

##参考記事：PWAとは？
[PWA（Progressive Web Apps）とは？メリットと実装事例について](https://digital-marketing.jp/seo/what-is-progressive-web-apps/)
外部の人にはいつもこの記事みせてます

#開発したもの

https://dev.elephancube.jp/201907pwatest/
更新：2019/7/18

##下準備
###https対応の領域を準備する
https://dev.elephancube.jp/201907pwatest/
レンタルサーバーのフリーSSLです。

###アイコン画像を512*512で準備する
透過が反映されるかの確認で、周りは透明、円の外周は半透明になっています。
![pwa_icon.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24420/a9e78343-d75f-00b3-843e-6432a0f0f0d1.png)

##1. アプリ登録とオフラインで使えるまで
###表示するHTMLファイル
追記していきますが、ベースはこのくらい

```html:index.html
<!doctype html>
<html lang="ja">

<head>
	<!-- Required meta tags -->
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

	<!--PWA動作用マニフェスト-->
	<link rel="manifest" href="manifest.json">
	<!-- Bootstrap CSS -->
	<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
		integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">

	<title>PWAのテスト</title>
</head>

<body>
	<div class="container">
		<h1>PWAのテスト</h1>
		<p>更新:2019/7/5</p>
	</div>

	<!-- Optional JavaScript -->
	<!-- jQuery first, then Popper.js, then Bootstrap JS -->
	<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
		integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
		crossorigin="anonymous"></script>
	<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"
		integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"
		crossorigin="anonymous"></script>
	<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"
		integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"
		crossorigin="anonymous"></script>
	<!--PWA動作用スクリプト-->
	<script src="pwatest.js"></script>
</body>

</html>

```

###Manifestの作成（＋アイコンの自動生成）
[Web App Manifest Generator](https://app-manifest.firebaseapp.com/)
このサイトを使って入力します。

![190705_pwa_1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24420/9fa516c3-8997-1b3c-d88d-1838dbf7b89f.png)


生成されたデータ

```json:manifest.json
{
  "name": "PWAのテストアプリケーション",
  "short_name": "PWA TEST",
  "theme_color": "#2196f3",
  "background_color": "#2196f3",
  "display": "fullscreen",
  "orientation": "portrait",
  "Scope": "/201907pwatest/",
  "start_url": "/201907pwatest/index.html?m=pwa",
  "icons": [
    {
      "src": "images/icons/icon-72x72.png",
      "sizes": "72x72",
      "type": "image/png"
    },
    {
      "src": "images/icons/icon-96x96.png",
      "sizes": "96x96",
      "type": "image/png"
    },
    {
      "src": "images/icons/icon-128x128.png",
      "sizes": "128x128",
      "type": "image/png"
    },
    {
      "src": "images/icons/icon-144x144.png",
      "sizes": "144x144",
      "type": "image/png"
    },
    {
      "src": "images/icons/icon-152x152.png",
      "sizes": "152x152",
      "type": "image/png"
    },
    {
      "src": "images/icons/icon-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "images/icons/icon-384x384.png",
      "sizes": "384x384",
      "type": "image/png"
    },
    {
      "src": "images/icons/icon-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ],
  "splash_pages": null
}
```

[ウェブアプリマニフェスト](https://developer.mozilla.org/ja/docs/Web/Manifest)
記述の詳細は、こちらのサイトが分かりやすいです。


###Service Workerの設定
[PWAをもっと簡単に初めてみる](https://qiita.com/poster-keisuke/items/6651140fa20c7aa18474)
このあたりの記事を参考に、まずは、Service Workerの設定ファイルを以下のように作成。
※ なお、このファイルに、index.htmlからリンクする必要はありません。

```javascript:sw.js
//キャッシュ名
var CACHE_NAME  = "PW-TEST-190705-01";

//キャッシュするファイル名
var urlsToCache = [
	"index.html",
	"https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css",
	"https://code.jquery.com/jquery-3.3.1.slim.min.js",
	"https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js",
	"https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js",
	"pwatest.js"
];

//インストール時処理
self.addEventListener('install', function(event) {
	event.waitUntil(
		caches
		.open(CACHE_NAME)
		.then(function(cache){
			return cache.addAll(urlsToCache);
		})
	);
});

// フェッチ時のキャッシュロード処理（2019/07/18 更新）
self.addEventListener('fetch', function(event) {
	event.respondWith(
		caches
			.match(event.request)
			.then(function(response) {
				if(response){
					return response;
				}
				return fetch(event.request);
			})
	);
});

```

次に、上記ファイルをロードする JavaScript を index.html に書きます。
今回は、index.html からリンクしている pwatest.js に記述しています。

```javascript:pwatest.js
$(function(){
	//Service Workerの登録
	if ('serviceWorker' in navigator) {
		navigator.serviceWorker.register('sw.js')
		.then(
		function (registration) {
				if (typeof registration.update == 'function') {
						registration.update();
				}
		})
		.catch(function (error) {
			console.log("Error Log: " + error);
		});
	}
});

```


###ここまでの確認
####Chrome の Developer Tool で確認
![190705_pwa_2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24420/ce3b7d70-5c55-1ae6-3caa-b36991b5faca.png)
Application → Manifest

- htmlからリンクされていないと、ここに表示がされません。
- start_url のパス間違いなどがあると、ここでエラー表示されます。

![190705_pwa_3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24420/67bc4f3d-e552-b7b3-2b6e-3ee4739d71c3.png)
Application → Service Workers

- htmlからリンクされていないと、ここに表示がされません。
- キャッシュファイルが足りていない（記述漏れがある）と、オフラインで閲覧できない旨のエラーがでます。

[Progressive Web App のデバッグ](https://developers.google.com/web/tools/chrome-devtools/progressive-web-apps?hl=ja)
Developer Toolsでの確認については、このサイトに詳しくのっています。


####Android9.0 実機で確認
※各種メッセージが英語なのは、端末の表示言語を英語にしているためです。
![2019-07-06 00.26.27.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24420/946f533b-6df2-0b4e-4cbe-87f5efa4452b.jpeg)
ポップアップがでて、
![2019-07-06 00.26.44.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24420/ce4a11dc-cfc8-26eb-ba94-db35e12c7382.jpeg)
インストール確認画面がでて、
![2019-07-06 00.27.27.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24420/e84678f1-db24-2837-c0d2-01d7e625a596.jpeg)
インストールされました。アイコンの透過も効いてる。
![2019-07-06 00.27.38.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24420/26154a20-f921-dd91-4efa-af7757d0268c.jpeg)

フルスクリーンで起動しました！
その後、５分以上たってから２回目のアクセスをするとキャッシュが効きますので、オフラインにしても動作します。

###iOS12.3実機で確認
![IMG_0004.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24420/2016b612-6922-33f8-49f2-246559060cbf.png)
Androidのように自動でポップアップが出ることはない。
![IMG_0005.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24420/d10cffae-8384-eb7c-7a92-262790617db3.png)
ホームに追加から、アイコン追加。
![IMG_0006.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24420/d0f21949-6ab9-10d2-5c74-387c2457052d.png)
このときのリンク名やURLはマニフェストからの情報が反映されます。
![IMG_0007.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24420/2bd90b0d-5d6f-7465-d1a3-f98c2383bfa5.png)
が．．．アイコンが、未設定状態
![IMG_0008.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24420/2bcca66e-efb9-dcce-9e84-65ca68194dd7.png)
起動すると、きちんとPWAのURLでアクセスされます。
オフラインでも動作しました。

[Designing Native-Like Progressive Web Apps For iOS](https://medium.com/appscope/designing-native-like-progressive-web-apps-for-ios-1b3cdda1d0e8)
iOSでアイコンを正しく設定するのは、PWAは関係なくhtmlのタグで apple-touch-icon の設定をします。このサイトのを参考に180x180のアイコン（透過なし）を設定します。

その他参考サイト
https://webdesign-trends.net/entry/7251


##2.キャッシュの動作確認
キャッシュされたことが確認できた後、ファイルを更新してみます。

|環境|htmlファイルを更新|jsファイルを更新|
|:---|:---|:---|
| Windows10 Chrome | リロードすると、内容更新される | リロードすると、内容更新される |
| Android 9.0　 | PWAアプリ再起動すると、内容更新される | <font color="Red">PWAアプリ再起動しても更新されない</font> |
| iOS | <font color="Red">PWAアプリ再起動しても更新されない</font> | <font color="Red">PWAアプリ再起動しても更新されない</font> |

都合良く、ネット繋がっていたら更新してくれるということは期待できなさそう。

###強制的にキャッシュを更新する
[Service Worker](https://qiita.com/propella/items/6500f76c9c1521878a6b)
こちらの記事にある postMessage を使えば良さそう。

sw.js へ追記

```javascript:sw.js
//メッセージを受け取ったとき
self.addEventListener('message', function(event) {
	switch (event.data) {
		case 'updateCache':
			event.waitUntil(
				caches
				.open(CACHE_NAME)
				.then(function(cache){
					return cache.addAll(urlsToCache);
				})
			);
		break;
	}
});

```

html側にボタンとスクリプト追記

```html:index.html
		<button class="btn btn-primary" id="btn_update">キャッシュ更新</button>

```

```javascript:pwatest.js
	$('#btn_update').click(function(){
		navigator.serviceWorker.controller.postMessage('updateCache');
		setTimeout(function(){
			location.reload();
		});
	});


```

これで、ボタンを押した瞬間にファイルが更新され、リロードすることが確認できました。

その他参考サイト
https://blog.htmlhifive.com/2018/08/27/pwa-cache-update/


##3.Webアクセスかアプリアクセスかの判別

```json:manifest.json
  "start_url": "/201907pwatest/index.html?m=pwa",
```

こんな感じでURLパラメータを付与したので、それで判別します。
判別法ロジックが雑ですが、以下のような形で判別できます。

```javascript:pwatest.js
	//PWAかどうかの判別
	var str = location.search;
	if(str.indexOf('m=pwa')==-1){
		//WEB MODE
	}else{
		//PWA MODE
	}

```


##4.リンク挙動とアラートの検証
いくつかリンクを作って検証

```html
<p><a class="btn" href="https://www.google.com/">GOOGLE（外部サイト）</a></p>
<p><a class="btn" href="page2.html">PWAスコープ内（キャッシュなし）</a></p>
<p><a class="btn" onclick="alert('アラートのテスト');">ALERT</a></p>

```

###Android9.0 実機で確認

![2019-07-17 01.24.43.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24420/a8af3861-00a5-10c7-4883-293e493a530e.jpeg)

スコープ内のリンク
![2019-07-17 01.25.00.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24420/89c813d4-acf4-5cc4-65cf-2f4b1003c407.jpeg)
通常のリンクと同じく遷移。
![2019-07-17 01.25.23.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24420/bd892d8b-64ae-df24-9f11-bd4c2bfedc72.jpeg)
ただしオフラインだと当たり前だが表示されない。

スコープ外のリンク
![2019-07-17 01.24.50.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24420/45723cfe-76a5-a61c-78f6-0ef122d45178.jpeg)
明示的にターゲット指定など何もしなくても、アプリ内ブラウザが起動して表示される。わかりやすい！

![2019-07-17 01.25.06.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24420/17e8905a-22ec-d046-61e5-0e7d4e3ca048.jpeg)
アラートはWEBと同じ。

###iOS12.3で確認
スコープ内のリンク
![IMG_0012.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24420/140b3099-dce4-6891-b92b-9c4c9a67d972.png)
ページ遷移。
![IMG_0014.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24420/e26fc1f2-17f4-88f2-ee1c-032b07c065be.png)
オフラインだとエラー

スコープ外のリンク
![IMG_0010.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24420/8a702925-ee61-59f0-c7ec-79f563a231c0.png)
プリ内ブラウザが立ち上がるが…左上に「完了」ボタンがない。
7/18追記：ServiceWorkerのfetchイベントの記述を少し変えたら、初回から「完了」ボタン出るようになった。謎。
![IMG_0011.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24420/66357d54-8176-eec1-6705-d15afdbad9ee.png)
１ページ遷移すると「完了」ボタンがでてくる。わかりづらい。

![IMG_0013.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24420/b68048e0-86dd-a63f-72c2-96676ec3673d.png)
アラートはWEBと同じ。


##5.Ajax通信の検証
~~同じスコープ内にapiを置いてアクセスしたら、ふつうにAJAX通信ができないことが判明！後日解決を図る。~~
7/18追記：Service Workerが分かっていなかった。記述を修正して動くようになりました。


#参考資料メモ
[PWAをもっと簡単に初めてみる](https://qiita.com/poster-keisuke/items/6651140fa20c7aa18474)
[PWA対応で実装したことまとめ (実物あり)](https://qiita.com/chikara_funabashi/items/7e456100e1ceb1bf3f20)
[PWA： ServiceWorkerを使って、キャッシュをコントロールする(オフラインハンドリング)](https://qiita.com/OMOIKANESAN/items/13a3dde525e33eb608ae)
[iOS 12.2のPWA対応まとめ](https://scrapbox.io/gyarasu/iOS_12.2%E3%81%AEPWA%E5%AF%BE%E5%BF%9C%E3%81%BE%E3%81%A8%E3%82%81)
[ScrapboxでのServiceWorkerとCacheの活用](https://scrapbox.io/daiiz/Scrapbox%E3%81%A7%E3%81%AEServiceWorker%E3%81%A8Cache%E3%81%AE%E6%B4%BB%E7%94%A8)
[Service Workerを使った消極的なキャッシュ](https://1000ch.net/posts/2015/service-worker-passive-cache.html)
[Debugging ServiceWorker](https://docs.google.com/presentation/d/1DKu4RZigLvM5XUq3ovsgffQBIHrro5-pii4qEJuyvrQ/edit#slide=id.p)