Djangoで作ったWEBアプリにPWAを導入する方法

# 概要
Django,Python初学者の自分がDjangoで作ったWEBアプリにPWAを導入する際にPWAについての日本語の記事はいくつかあれど、DjangoでPWAを導入している例を挙げているような記事がほとんどなく、導入に非常に手間取ったので自分用の忘備録としても記事に残しておこうと思った次第です。

# PWAとは
Progressive Web Appsの略で、googleが開発したWEBアプリなどをネイティブなアプリケーションのように動作させるモバイル向けの仕組み、と理解しています。
これを導入することによっていちいちブラウザからアクセスしてもらう必要がなくなり、ホームに追加されたアイコンをタップするだけでWEBアプリにアクセスしてもらうことができます。


# DjangoのWEBアプリに導入する
本題です。
今回はPWAを導入してデスクトップ版、Android版のChromeでPWAをインストールできるとこまでを目標とし、バージョンはDjango 2.2.4で作っていきます。
完成したコードはGithubに公開しています。
https://github.com/kitune-chan-250r/pwa-sample

### Djangoのプロジェクトとアプリを作成

```
django-admin startproject mypwa
```
mypwaへ移動しアプリケーションpwatestを作成

```
python manage.py startapp pwatest
```

#####mypwa/mypwa/setting.py にアプリを追加

```python
# 省略
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'pwatest', #追加
]
# 省略
```
サーバーの起動

```
python manage.py runserver
```
下記のように表示されれば問題ないです。

```
Watching for file changes with StatReloader                                                                            
Performing system checks...                                                                                            
                                                                                                                       
System check identified no issues (0 silenced).                                                                        
                                                                                                                       
You have 17 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.                                                                                        
Run 'python manage.py migrate' to apply them.                                                                          
October 22, 2019 - 23:52:56                                                                                                                                                             
Starting development server at http://127.0.0.1:8000/                                                                  
Quit the server with CTRL-BREAK. 
```
### 最低限のwebアプリケーションを作成
##### mypwa/mypwa/urls.py
```python
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('pwatest.urls'))
]
```
##### mypwa/pwatest/urls.py (新規作成します)

```python
from django.urls import path, include
from .views import *

urlpatterns = [
    path('', index, name='index')
]
```
##### mypwa/pwatest/views.py

```python
from django.shortcuts import render

def index(request):
	return render(request, 'index.html')
```
##### mypwa/pwatest/templates/index.html (templatesフォルダを作成し、その中にindex.htmlを作成)

```html
<!DOCTYPE html>
<html>
<head>
	<title></title>
</head>
<body>
	<h1>PWA test page</h1>
</body>
</html>
```

この状態で http://127.0.0.1:8000/ へアクセスするとでかでかとPWA test pageと書かれているページが出現するはずです。


### PWAに必要な設定、ファイルを追加
ここまで作ってきた最低限のwebアプリにpwaを導入していきます。
始めにPWAをインストールできるようにするために必要最低限の項目がこちらです（https://developers.google.com/web/fundamentals/app-install-banners より抜粋）

```
Includes a web app manifest that includes:
    ・short_name or name
    ・icons must include a 192px and a 512px sized icons
    ・start_url
    ・display must be one of: fullscreen, standalone, or minimal-ui
Served over HTTPS (required for service workers)
Has registered a service worker with a fetch event handler 
```
簡単に説明すると、manifest.jsonにはshort_name、nameのどちらか（自分の環境では両方必要でした）、192px と 512pxサイズのアイコンの設定、start_url、display（fullscreen, standalone, minimal-uiのうちどれか一つ）が含まれており、HTTPSで配信されていて且つservice workerが登録されていること、とのことです。（localhostの場合はhttpでも動作するようです）
この条件を満たすよう先ほどのwebアプリに手を加えていきます。

##### mypwa/pwatest/templates/index.html
```html
<!DOCTYPE html>
<html>
<head>
	<title>pwa test</title>
        <!-- ここから -->
	<link rel="manifest" href="static/manifest.json">
	<script>
		if ('serviceWorker' in navigator) {
  			navigator.serviceWorker.register('sw.js').then(function() { console.log('Service Worker Registered'); });
			}
	</script>
　　　　<!-- ここまで追加 -->
</head>
<body>
	<h1>PWA test pwage</h1>
</body>
</html>
```

##### mypwa/mypwa/setting.py (一番下に追加)
```python
#省略
STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'static'),
)
```
##### mypwa/mypwa/urls.py

```python
from django.contrib import admin
from django.urls import path, include

#追加
from django.views.generic import TemplateView
from django.views.decorators.cache import cache_control

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('pwatest.urls')),
    #追加
    path('sw.js', (TemplateView.as_view(template_name="sw.js",
    content_type='application/javascript', )), name='sw.js'),
]
```
↑このurls.pyの部分が一番重要な部分であり、調べてもほとんど出てこなくて詰まったポイントでした。

ここからはPWAに必要なファイルを作成していきます。
##### mypwa/static/manifest.json (staticフォルダを作成し、その中にmanifest.jsonを作成)

```json
{
  "background_color": "#1f1f1f",
  "description": "pwa test",
  "display": "standalone",
  "theme_color": "aliceblue",
  "icons": [{
    "src": "icon-192x192.png",
    "sizes": "192x192",
    "type": "image/png"
  },{
    "src": "icon-512x512.png",
    "sizes": "512x512",
    "type": "image/png"
  }],
  "name": "pwaapp",
  "short_name": "pwaapp",
  "start_url": "http://127.0.0.1:8000"
}
```

##### mypwa/pwatest/templates/sw.js (staticではなくtemplatesフォルダ内に作成する点に注意してください)

```javascript
self.addEventListener('fetch', function(event) {});
```

最後にstaticフォルダ内にicon-192x192.pngとicon-512x512.pngを追加するとPWAの導入は完了です。

# 動作確認
#### PC
![スクリーンショット (210).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214955/c309acd0-51e0-7f00-8176-482487953086.png)

導入が成功していればPC版のChromeでアクセスした場合にURLの右端に+マークが出てくるようになり、それをクリックすることによってインストールすることが可能になります。インストール後はデスクトップに設定したアイコンが出現しそれをクリックすることによってwebアプリに直接アクセスすることが可能になります。

#### Android版Chrome
herokuなどのPaaSにデプロイして確認することもできます。その場合、ホームにアプリ追加バナーが出るようになります、通常は5分以上の間隔をあけて2度目にアクセスした際に表示されますが、デバッグする際は chrome://flags/#bypass-app-banner-engagement-checks にアクセスして有効化することで1度目のアクセスでバナーが表示されるようになります。

# おまけ
iOS端末しか持っていない！デバッグできねぇじゃん！って方もいるかと思います（自分です）
自分の環境ではNoxなどのエミュレータでも動作確認することができず非常に困っていたのですが、Chromeの拡張機能にApkOnlineというブラウザ上で動作するAndroidエミュが存在し、これでPWAのインストールまで動作確認することができます、びっくりですね（ただし動作は非常に遅いです）もしどうしようもなければ試してみてください。

# 最後に
初心者が書いた初Qiita記事でしたが少しはお役に立てたでしょうか、自分は深いところの解説ができないのでdjango + pwaの日本語記事が増えればなと思っています。


Stream apiを使ったツイートが上から下に流れないタイプのTwitterクライアントを開発しています。
ぜひ使ってみてください。

https://newtwitterclient.azurewebsites.net/

#参考ページ
Add to Home Screen (英語)
https://developers.google.com/web/fundamentals/app-install-banners
はじめてのプログレッシブウェブアプリ
https://developers.google.com/web/fundamentals/codelabs/your-first-pwapp
【3ステップではじめる】PWAによる「ホーム画面に追加」バナーの実装
https://amymd.hatenablog.com/entry/2017/10/12/001612

スタックオーバーフローも参考にしたんですがどこかにいきました....

##### 蛇足
Stream apiを使ったツイートが上から下に流れないタイプのTwitterクライアントを開発しています。
ぜひ使ってみてください。

https://newtwitterclient.azurewebsites.net/