How to easily make PWA compatible with offline display
Tags: javascript, webdev, pwa, html
Published: 2021-04-30 03:08:37

Hello.  Suddenly, **PWA's offline display support is mandatory.**

 The Chrome update will review the criteria for installing web apps as PWAs.
# Announcement from Google
- Starting with **Chrome 89**, if the PWA does not return a valid response when offline, **a warning will be displayed in the developer tools console.**
- Starting with **Chrome 93** (scheduled to be released later this year), **web apps that don't work properly offline** can no longer be installed as PWAs.

In other words, make it available offline.  That's right.
 Currently, there aren't many PWA sites that support offline, so we should take immediate action.

It's very easy to get around this problem, just have an offline page.  Well, of course.
 So this time, I will make a template for offline support with HTML + JS.
# Implementation
## Note
**SSL support is required** for PWA.  Please be careful.  (For the time being, localhost works even with non-SSL)
### Constitution

```
html
├ img
│ └ logo.png
├ index.html
└ sw.js
```


## manifest.json
Create the usual manifest.json to recognize it as a PWA.  Below is the template.

```json
{
    "name": "PWA Offline Test",
    "short_name": "PWA",
    "icons": [
        {
            "src": "/img/icon.png",
            "sizes": "144x144",
            "type": "image/png"
        }
    ],
    "start_url": "/",
    "display": "standalone",
    "theme_color": "#99d9ea"
}
```

There are a lot of detailed ways to write Manifest.json, so please refer to it.
## ServiceWorker
Write the Service Worker required for PWA to work.

```js
// Version definition
var CACHE_VERSION = 'ca-v1';
var DISP_VERSION = 'ca-d-v1';

// Directory to be cached (css / js is added individually)
var resources = [
  '/',
  '/img'
];

// Add cache
self.addEventListener('install', function (event) {
  console.log('ServiceWorker Install');
  event.waitUntil(
    caches.open(CACHE_VERSION)
      .then(function (cache) {
        console.log('cache.addAll');
        cache.addAll(resources);
      })
  );
});
// Shiw cash
self.addEventListener('fetch', function (event) {
  console.log('ServiceWorker fetch');
  event.respondWith(
    // Check if cache exists
    caches.match(event.request)
      .then(function (response) {
        if (response) {
          return response;
        } else {
          // If there is no cache, put it in the cache
          return fetch(event.request)
            .then(function (res) {
              return caches.open(DISP_VERSION)
                .then(function (cache) {
                  console.log('cache.put');
                  cache.put(event.request.url, res.clone());
                  return res;
                });
            })
            .catch(function () {
              // do nothing
            });
        }
      })
  );
});
// Delete old cache
self.addEventListener('activate', function (event) {
  console.log('activate ServiceWorker');
  event.waitUntil(
    caches.keys()
      .then(function (keyList) {
        return Promise.all(keyList.map(function (key) {
          if (key !== CACHE_VERSION && key !== DISP_VERSION) {
            console.log('cache.delete');
            return caches.delete(key);
          }
        }));
      })
  );
  return self.clients.claim();
});
```

## HTML Template
Write HTML to display offline.  At that time, let's read the one I wrote earlier.
Below template

```html
<head>
    <link rel="manifest" href="/manifest.json">
    <!-- Character code setting & mobile support -->
    <meta charset="utf8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <script>
        // Load ServiceWorker
        window.addEventListener('load', function () {
            if ('serviceWorker' in navigator) {
                navigator.serviceWorker.register('/sw.js')
                    .then(function (registration) {
                        return registration.pushManager.getSubscription().then(function (subscription) {
                            console.log("subscription", subscription)
                            if (subscription) {
                                return subscription
                            }
                            return registration.pushManager.subscribe({
                                userVisibleOnly: true
                            })
                        })
                    }).then(function (subscription) {
                        var endpoint = subscription.endpoint
                        console.log("pushManager endpoint:", endpoint)
                    }).catch(function (error) {
                        console.log("serviceWorker error:", error)
                    })
            }
        })
    </script>
</head>
<body>
    PWA offline display test (front page)
    <br>
    <a href="https://hoge.com/index2.html">To the second page</a>
    <!-- You can also transition pages.  At that time, it may not work well unless Service Worker is loaded on the transition destination page as well. -->
    <br>
    <img src="/icon.png">
</body>
</html>
```

You can also transition pages.  At that time, it may not work well unless Service Worker is loaded on the transition destination page as well.

This completes the implementation.
Let's try it.

# Operation check
 A pop-up prompting you to install will be displayed at the bottom of the screen, so try installing it.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dsittio8xcj6c1eyy6zw.jpeg)![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5gk0ny07kdzlquwwywg6.jpeg)
When the installation is complete, a notification will be displayed. Open it and check the display.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6zj0nctoappt4t8kpf6g.jpeg)
If you can display it properly, **try setting it to airplane mode.**
It is OK if you can display it without any problem even if you open the application with this.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2u84disj0oauu3mian9w.jpeg)
You can display it properly.  There is no problem with page transitions.
Please also check the blog and Twitter if you like :D
**[Twitter @tomox0115](https://twitter.com/tomox0115)**
**[My BLOG](https://0115765.com)**