Skip to content

Instantly share code, notes, and snippets.

@ekafyi
Last active December 10, 2021 16:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ekafyi/b0f8d8c58bc42aa6df02d925d0b25f49 to your computer and use it in GitHub Desktop.
Save ekafyi/b0f8d8c58bc42aa6df02d925d0b25f49 to your computer and use it in GitHub Desktop.
PWA workshop exercises solution

Exercise 1 — Solution

manifest.json

{
  "name": "Cat of the Day",
  "short_name": "CatOfTheDay",
  "description": "Sample web app for the PWA workshop",
  "icons": [
    {
      "src": "https://cdn.glitch.me/6bd90b97-bb15-4dd3-aae1-e56bb3d1e206%2Ficon-192x192.png",
      "sizes": "192x192"
    },
    {
      "src": "https://cdn.glitch.me/6bd90b97-bb15-4dd3-aae1-e56bb3d1e206%2Ficon-512x512.png",
      "sizes": "512x512"
    }
  ],
  "start_url": "/",
  "display": "standalone",
  "background_color": "#a8dadc"
}

index.html

<head>
  <!-- ... other head content -->

  <link rel="manifest" href="manifest.json" />
</head>

Exercise 2 — Solution

sw.js

self.addEventListener("install", (event) => {
  console.log("[SW] install", event);
});

self.addEventListener("activate", (event) => {
  console.log("[SW] activate", event);
});

self.addEventListener("fetch", (event) => {
  console.log("[SW] fetch", event);
});

index.html

<head>
  <!-- ... -->
  
  <script>
    // ...
    if ("serviceWorker" in navigator) {
      navigator.serviceWorker
        .register("/sw.js")
        .then((reg) => {
          console.log("Service Worker registered", reg);
        })
        .catch((err) => {
          console.error("Service Worker not registered", err);
        });
    } else {
      console.log("Service Worker not supported");
    }
  </script>
</head>

Exercise 3 — Solution

sw.js

const CACHE_NAME = "pwa-part-1";

const FILES_TO_CACHE = ["/", "style.css"];

self.addEventListener("install", (event) => {
  console.log("[SW] install", event);
  event.waitUntil(
    caches
      .open(CACHE_NAME)
      .then((cache) => {
        return cache.addAll(FILES_TO_CACHE);
      })
      .catch((err) => {
        console.warn("[SW] Precaching failed", err);
      })
  );
});

self.addEventListener("activate", (event) => {
  console.log("[SW] activate", event);
});

self.addEventListener("fetch", (event) => {
  console.log("[SW] fetch", event);
  event.respondWith(
    caches
      .match(event.request)
      .then((response) => {
        // return matching cache item
        if (response) {
          return response;
        }
        // make network request
        return fetch(event.request);
      })
      .catch((err) => {
        console.warn(err);
      })
  );
});

Exercise 4 — Solution

sw.js (1)

+ const OFFLINE_URL = "offline.html";

- const FILES_TO_CACHE = ["/", "/style.css", "/script.js"];
+ const FILES_TO_CACHE = ["/", "/style.css", "/script.js", OFFLINE_URL];

sw.js (2)

self.addEventListener("fetch", (event) => {
  console.log("[SW] fetch", event);
  event.respondWith(
    caches
      .match(event.request)
      .then((response) => {
        // return matching cache item
        if (response) return response;
        // make network request
        return fetch(event.request);
      })
      .catch((err) => {
        console.warn(err);
+        if (event.request.mode === "navigate") {
+          return caches.match(OFFLINE_URL);
+        }
      })
  );
});

Exercise 5 — Solution

script.js

- document.querySelector("#footer-text").innerText = "v1";
+ document.querySelector("#footer-text").innerText = "v2";

sw.js (1)

- const CACHE_NAME = "pwa-part-1";
+ const CACHE_NAME = "pwa-part-2";

sw.js (2)

self.addEventListener("activate", (event) => {
  console.log("[SW] activate", event);
  event.waitUntil(
    caches
      .keys()
      .then((keyList) => {
        return Promise.all(
          keyList.map((key) => {
            if (key === CACHE_NAME) {
              return;
            }
            return caches.delete(key);
          })
        );
      })
      .catch((err) => {
        console.warn("[SW] Delete old caches failed", err);
      })
  );
});

Exercise 6 — Solution

sw.js

self.addEventListener("fetch", (event) => {
  console.log("[SW] fetch", event);
  event.respondWith(
    caches
      .match(event.request)
      .then((response) => {
        // return matching cache item
        if (response) return response;
        // make network request
-        return fetch(event.request);
+        return fetch(event.request)
+          .then((response) => {
+            return caches.open(CACHE_NAME).then((cache) => {
+              cache.put(event.request.url, response.clone());
+              return response;
+            });
+          });
      })
      .catch((err) => {
        console.warn(err);
        if (event.request.mode === "navigate") {
          return caches.match(OFFLINE_URL);
        }
      })
  );
});

Exercise 7 — Solution

script.js

if ("connection" in navigator) {
  if (navigator.connection.effectiveType !== "4g") {
    showSlowConnectionWarning();
  }

  navigator.connection.addEventListener("change", (event) => {
    console.log("Network changed to...", event.currentTarget);
    if (event.currentTarget.effectiveType !== "4g") {
      showSlowConnectionWarning();
    } else {
      hideSlowConnectionWarning();
    }
  });
} else {
  console.log("Network Information not supported");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment