Search code examples
progressive-web-appsvitesveltekit

SvelteKit WebApp to PWA (Progressive Web App), How to do it in the most simple way possible?


Introduction:

I am developing a webapp using the SvelteKit framework,
I implemented some basic functionality for making accessible even to mobile devices:

  • I made it responsive
  • implemented light/dark themes
  • refactored the code

So basically I think I have a ready-to-use website
that can easily be usable on mobile devices.


What I want?:

I want that my website to be downloaded as a apk
to make it working also Offline
and hopefully even be publishable on Google Play Store.


My research:

I did some research before asking,
and I found that we can use a concept called PWA, a acronym for Progressive Web App.


What is the problem?

It's seems easy, but in reality,
It turn out to be very difficult to implement using framework like 'SvelteKit'


Question?:

Is there any simpler way than these before,
to transform a vite sveltekit webapp to a PWA?

I want also to be simple because I want to
focus more on Business/App logic than implementing PWA logic.


What things, the answer should solve?

  • No need to recreate the app from scratch, using native technologies
  • No need to complex the code using new components, beside the DOM original ones (NO Condova/Capacitor Approach)
  • One source code for Android and IOS
  • Offline / Caching Ability
  • Download Ability
  • easy to implement on new, and even on more old/complex projects.
  • it should work with SvelteKit/ViteJS (even with TypeScript and Libraries such as TailwindCSS)
  • No need to disactivate SSR.
  • The configuration should be done one time, or the least times possible.

What I tried, if interest you but these are failed tryies (you can skip this part):

- First and obvious way, code it by yourself from scratch everytime
but 'SvelteKit' has a lot of scenarios that you need to consider since it leverage 'SSR'.
But as you know is time consuming
to focus on making PWA functionality to work
than using that time to make the app even more better or solving some bugs.

- use a PWA library called Workbox create by Google itself,
here their github https://github.com/GoogleChrome/workbox
and even with this library this process is still difficult, especially using SvelteKit framework.

- using a PWA library, based on top of Workbox, called Vite PWA.
The landing page of this library use as marketing phrase "Zero-config and framework-agnostic PWA Plugin for Vite".
Theoretically, it should be true, At the start seems to do his job,
but in my experience it contains some bugs that you definitely doesn't want to deal with...
for example one of them is this: https://github.com/vite-pwa/vite-plugin-pwa/issues/40
It creates bugs that don't make sense, basically making your app over-complex for nothing.

- and much more...

all of these are failed examples,
I hope you will find a better way than these


Solution

  • here is the easiest way to make a PWA from SvelteKit, that should work always, without any library:

    1. create a folder called service-worker

    you can't use another name, it's important to use specifically this


    1. put this new folder inside your /src folder

    DON'T insert it inside '/lib' or '/routes', but inside their parent '/src'


    1. inside the new folder /service-worker, create a new file called index.ts
      (or index.js if you don't like using Typescript)

    below you can see a image, I created for you, to make you understand visually the structure (semplified version):


    1. copy and paste this code in the file you created in step n. 3:

    /// <reference types="@sveltejs/kit" />
    
    // @ts-nocheck
    import { build, files, version } from '$service-worker';
    
    // Create a unique cache name for this deployment
    const CACHE = `cache-${version}`;
    
    const ASSETS = [
        ...build, // the app itself
        ...files  // everything in `static`
    ];
    
    self.addEventListener('install', (event) => {
        // Create a new cache and add all files to it
        async function addFilesToCache() {
            const cache = await caches.open(CACHE);
            await cache.addAll(ASSETS);
        }
    
        event.waitUntil(addFilesToCache());
    });
    
    self.addEventListener('activate', (event) => {
        // Remove previous cached data from disk
        async function deleteOldCaches() {
            for (const key of await caches.keys()) {
                if (key !== CACHE) await caches.delete(key);
            }
        }
    
        event.waitUntil(deleteOldCaches());
    });
    
    self.addEventListener('fetch', (event) => {
        // ignore POST requests etc
        if (event.request.method !== 'GET') return;
    
        async function respond() {
            const url = new URL(event.request.url);
            const cache = await caches.open(CACHE);
    
            // `build`/`files` can always be served from the cache
            if (ASSETS.includes(url.pathname)) {
                return cache.match(url.pathname);
            }
    
            // for everything else, try the network first, but
            // fall back to the cache if we're offline
            try {
                const response = await fetch(event.request);
    
                if (response.status === 200) {
                    cache.put(event.request, response.clone());
                }
    
                return response;
            } catch {
                return cache.match(event.request);
            }
        }
    
        event.respondWith(respond());
    });

    like I said you don't have to understand these lines of code, just copy and paste and they should work in most of cases (it's written by SvelteKit maintainers themselves)

    for more details about this code...
    I got this code here: https://kit.svelte.dev/docs/service-workers#inside-the-service-worker


    all the files inserted in this folder are treated by default as service-workers...

    is cool, right?

    service workers aren't used only for PWA,
    but also for much more...
    for example for making your app work even in background,
    but thats is definitely another topic.


    1. create your manifest.json file, inside your static folder (you can't use another folder unfortunately)

      this part can't be automated,
      but is easy to get it...

      the most important data that you need to add for the most basic app are:
    • icons (with at least one image)
    • name
    • maybe also "display": "standalone" to make the webapp feel like an android app by eliminating the browser bar.
      here is the simplest json markup that you can use as starting point:
    {
      "name": "APP NAME",
      "description": "YOUR DESCRIPTION",
      "display": "standalone",
      "start_url": "/",
      "icons": [
        {
          "src": "icons/myIcon512.png",
          "sizes": "512x512",
          "type": "image/png",
          "purpose": "any maskable"
        }
      ]
    }
    

    if you want a more detailed manifest.json see this doc: https://developer.mozilla.org/en-US/docs/Web/Manifest


    1. add this line in your app.html
    <link rel="manifest" href="/manifest.json">
    

    1. Congratulations!, you made your first SvelteKit webapp to be a PWA!
    • refresh your windows to see that a new install button appear
    • and in your mobile phone your app works even when opened without internet (in the second opening of your app)

    as you see this way is easy/fast/efficient,
    and it takes less than one minute!
    and the most important thing: no need for external libraries

    have a nice coding day!