Cara Membuat Progressive Web App (PWA) pada Blogger

Cara Membuat Progressive Web App (PWA) pada Blogger

Cara membuat PWA, Progressive Web App pada Blogger, Mempercepat loading blog dengan PWA
Cara Membuat Progressive Web App (PWA) pada Blogger

Progressive Web App Blogger - Pernahkah Anda melihat sebuah Pop Up notifikasi "Add to Home Screen" pada blog yang sedang Anda kunjungi? Jika iya, itu artinya anda sedang melihat PWA dari blog tersebut bekerja.

Apa itu PWA?

PWA adalah singkatan dari Progressive Web App, sebuah aplikasi yang dibangun dengan melakukan optimasi pada sebuah website. PWA merupakan salah satu solusi untuk membuat akses website menjadi lebih cepat

Beberapa web besar seperti Pinterest, twitter dan facebook juga menggunakan PWA untuk kemudahan penggunanya. Seperti namanya PWA adalah aplikasi website progresif, yang tentu berbeda dengan Web App, dan juga Mobile App.

Lalu, apakah perbedaan ketiganya? Berikut ini karakteristik yang dimiliki oleh masing-masing aplikasi:

Web App

  • Dibuat dengan mengandalkan browser dan cross platform
  • Update berjalan otomatis
  • Hanya berjalan dalam kondisi online
  • Fitur terbatas
  • Biaya development murah

Aplikasi Mobile

  • Dibuat untuk platform tertentu menggunakan bahasa pemrograman khusus, seperti Swift untuk iOS dan Java/Kotlin untuk Android
  • Update harus dilakukan manual
  • Bisa berjalan dalam kondisi offline
  • Fitur lengkap yang menjamin kenyamanan pengguna
  • Biaya development cenderung mahal

Progressive Web App

  • Dibuat mengandalkan browser dan cross platform
  • Update berjalan otomatis
  • Bisa berjalan dalam kondisi offline
  • Menghadirkan UX yang lebih intuitif dari web app
  • Biaya development murah

Cara Membuat Progressive Web App (PWA) pada Blogger

Untuk membuat Progressive Web App, Anda membutuhkan beberapa fitur tambahan seperti service workers yang fungsinya membuat Blog tetap berjalan dalam keadaan offline. Kemudian Push Notification yang tampil ketika Blog dibuka. Anda juga bisa menambahkan notifikasi/menu 'Add to Home Screen" sehingga pengunjung bisa secara langsung menginstall aplikasi dari menu browser.

Mungkin tutorial ini agak panjang dan membingungkan. Pastikan Anda mengikuti langkah - langkah yang saya jelaskan dengan benar tanpa ada yang terlewat.

Penting! Untuk bisa membuat PWA ini blog Anda harus sudah menggunakan domain TLD yang sudah terhubung ke layanan Cloudflare dan tidak akan bekerja pada domain blogspot.com

Persiapan

Sebelum melanjutkan Anda wajib menyiapkan beberapa bahan dibawah ini :

  1. Logo blog format .png dengan ukuran 512x512 pixel
  2. 5 Screenshot blog format .png
  3. Buat akun Github jika belum punya
  4. DNS Management terhubung dengan Cloudflare

Upload Icon ke Github

  1. Ubah nama logo yang sebelumnya sudah Anda siapkan dengan android-icon-512x512.png
  2. Generate favicon online di favicon-generator.org
  3. Download favicon yang berhasil di generate
  4. Extract file .zip dan hapus file yang tidak terpakai yaitu browserconfig.xml dan manifest.json
  5. Buka akun Github Anda dan buat Repository baru misalnya icon-kuymase
  6. Upload semua favicon termasuk icon original android-icon-512x512.png yang berjumlah sekitar 26 file pada main branch tanpa folder apapun (hanya icon)
  7. Jika sudah silahkan Simpan atau Commit

Upload Screenshot ke Github

  1. Siapkan 5 Screenshot tampilan blog Anda yang sebelumnya
  2. Rename masing - masing file secara berurutan menjadi :
  3. scr1.png
    scr2.png
    scr3.png
    scr4.png
    scr5.png
  4. Buka repository Github yang sama dengan sebelumnya
  5. Upload semua file tanpa folder sama seperti sebelumnya
  6. Silahkan Commit changes

Membuat Workers pada Cloudflare

Ada 4 Workers yang akan kita buat. Untuk memudahkan saat ROUTE nantinya, saya sarankan Anda untuk menamai masing - masing workers seperti berikut :

  • main-kuymase
  • manifest-kuymase
  • serviceworker-kuymase
  • offline-kuymase

Ganti kuymase dengan nama blog Anda

Main Workers

  1. Login ke Akun Cloudflare Anda
  2. Buka Domain aktif
  3. Pilih Workers kemudian Manage Workers
  4. Buat workers baru dan rename menjadi main-bloganda contoh main-kuymase
  5. Hapus semua script default dan ganti dengan script dibawah ini :
  6. addEventListener("fetch", event => {
      event.respondWith(handleRequest(event))
    })
    
    //const BUCKET_NAME = "main"
    const BUCKET_URL = `https://cdn.statically.io/gh/kuymase/icon-kuymase`
    
    async function serveAsset(event) {
      const url = new URL(event.request.url)
      const cache = caches.default
      let response = await cache.match(event.request)
    
      if (!response) {
        response = await fetch(`${BUCKET_URL}${url.pathname}`)
        const headers = { "cache-control": "public, max-age=14400" }
        response = new Response(response.body, { ...response, headers })
        event.waitUntil(cache.put(event.request, response.clone()))
      }
      return response
    }
    
    async function handleRequest(event) {
      if (event.request.method === "GET") {
        let response = await serveAsset(event)
        if (response.status > 399) {
          response = new Response(response.statusText, { status: response.status })
        }
        return response
      } else {
        return new Response("Method not allowed", { status: 405 })
      }
    }

    Silahkan ganti github-user dengan username Github Anda
    Ubah kuymase/icon-kuymase dengan nama Repository Anda

  7. Kemudian klik Save and Deploy

Manifest.json

  1. Buat workers baru dengan cara yang sama dan rename menjadi manifest-namablog contoh: manifest-kuymase
  2. Hapus semua script default dan ganti dengan dibawah ini :
  3. addEventListener("fetch", event => {
      const data = {
        name: "KuyMase Blog",
        short_name: "KuyMase Blog",
        description: "Install KuyMase Blog App right Now!",
        display: "standalone",
        prefer_related_applications: false,
        start_url: "\/?utm_source=homescreen",
        scope: "\/",
        background_color: "#2196f3",
        theme_color: "#2196f3",
        icons: [
          {
          src: "\/main\/android-icon-512x512.png",
          sizes: "512x512",
          type: "image\/png",
          density: "4.0",
          purpose: "any maskable"
          },
          {
          src: "\/main\/android-icon-192x192.png",
          sizes: "192x192",
          type: "image\/png",
          density: "4.0",
          purpose: "any maskable"
          },
          {
          src: "\/main\/apple-icon-144x144.png",
          sizes: "144x144",
          type: "image\/png",
          density: "3.0",
          purpose: "any maskable"
          },
          {
          src: "\/main\/android-icon-96x96.png",
          sizes: "96x96",
          type: "image\/png",
          density: "2.0",
          purpose: "any maskable"
          },
          {
          src: "\/main\/android-icon-72x72.png",
          sizes: "72x72",
          type: "image\/png",
          density: "1.5",
          purpose: "any maskable"
          },
          {
          src: "\/main\/android-icon-48x48.png",
          sizes: "48x48",
          type: "image\/png",
          density: "1.0",
          purpose: "any maskable"
          },
          {
          src: "\/main\/android-icon-36x36.png",
          sizes: "36x36",
          type: "image\/png",
          density: "0.75",
          purpose: "any maskable"
          }
        ],
        shortcuts: [
          {
          name: "KuyMase Home",
          short_name: "KuyMase Home",
          description: "Temukan tutorial dan informasi menarik seputar blogging dan teknologi.",
          url: "\/?utm_source=homescreen",
          icons: [
              {
              src: "\/main\/android-icon-192x192.png",
              sizes: "192x192"
              }
            ]
          },
          {
          name: "Blogger Widgets",
          short_name: "Blogger Widgets",
          description: "Tutorial widget blogger.",
          url: "\/search\/label\/Widgets?utm_source=homescreen",
          icons: [
              {
              src: "\/main\/android-icon-192x192.png",
              sizes: "192x192"
              }
            ]
          }
        ],
        screenshots: [
          {
          src: "\/main\/scr1.png",
          type: "image\/png",
          sizes: "540x720"
          },
          {
          src: "\/main\/scr2.png",
          type: "image\/png",
          sizes: "540x720"
          },
          {
          src: "\/main\/scr3.png",
          type: "image\/png",
          sizes: "540x720"
          },
          {
          src: "\/main\/scr4.png",
          type: "image\/png",
          sizes: "540x720"
          },
          {
          src: "\/main\/scr5.png",
          type: "image\/png",
          sizes: "540x720"
          }
        ],
        serviceworker: {
          src: "\/sw.js"
        }
      }
    
      const json = JSON.stringify(data, null, 2)
    
      return event.respondWith(
        new Response(json, {
          headers: {
            "content-type": "application/json;charset=UTF-8"
          }
        })
      )
    })
    
  4. Ubah yang saya tandai sesuai keinginan Anda termasuk warna
  5. Kemudian Save and Deploy

Service Worker

  1. Buat lagi workers baru dan rename menjadi serviceworker-namablog contoh: serviceworker-kuymase
  2. Ganti script default dengan script dibawah ini :
  3. const js = `
    importScripts('https://storage.googleapis.com/workbox-cdn/releases/5.1.2/workbox-sw.js');
    if (workbox) {
    workbox.core.skipWaiting();
    workbox.core.clientsClaim();
    workbox.core.setCacheNameDetails({
      prefix: 'thn-sw',
      suffix: 'v22',
      precache: 'install-time',
      runtime: 'run-time'
    });
    
    const FALLBACK_HTML_URL = '/offline.html';
    const version = workbox.core.cacheNames.suffix;
    workbox.precaching.precacheAndRoute([{url: FALLBACK_HTML_URL, revision: null},{url: '/manifest.json', revision: null},{url: '/main/favicon.ico', revision: null}]);
    
    workbox.routing.setDefaultHandler(new workbox.strategies.NetworkOnly());
    
    workbox.routing.registerRoute(
        new RegExp('.(?:css|js|png|gif|jpg|svg|ico)$'),
        new workbox.strategies.CacheFirst({
            cacheName: 'images-js-css-' + version,
            plugins: [
                new workbox.expiration.ExpirationPlugin({
                    maxAgeSeconds: 60 * 24 * 60 * 60,
                    maxEntries:200,
                    purgeOnQuotaError: true
                })
            ],
        }),'GET'
    );
    
    workbox.routing.setCatchHandler(({event}) => {
          switch (event.request.destination) {
            case 'document':
            return caches.match(FALLBACK_HTML_URL);
          break;
          default:
            return Response.error();
      }
    });
    
    self.addEventListener('activate', function(event) {
      event.waitUntil(
        caches
          .keys()
          .then(keys => keys.filter(key => !key.endsWith(version)))
          .then(keys => Promise.all(keys.map(key => caches.delete(key))))
      );
    });
    
    }
    else {
        console.log('Oops! Workbox did not load');
    }
    `
    
    async function handleRequest(request) {
      return new Response(js, {
        headers: {
          "content-type": "application/javascript;charset=UTF-8",
        },
      })
    }
    
    addEventListener("fetch", event => {
      return event.respondWith(handleRequest(event.request))
    })
    
  4. Kemudian klik Save dan Deploy

Offline

  1. Sekali lagi buat workers baru dengan nama offline-namablog contoh: offline-kuymase
  2. Ganti script default dengan script dibawah ini :
  3. const html = `<!DOCTYPE html>
    <html>
    
    <head>
      <!--[ Meta Tags ]-->
      <title>Oops, You're Offline!</title>
      <meta charset='UTF-8'/>
      <meta content='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0' name='viewport'/>
      <meta content='IE=edge' http-equiv='X-UA-Compatible'/>
    
      <!--[ Theme Color ]-->
      <meta content='#2196f3' name='theme-color'/>
      <meta content='#2196f3' name='msapplication-navbutton-color'/>
      <meta content='#2196f3' name='apple-mobile-web-app-status-bar-style'/>
      <meta content='true' name='apple-mobile-web-app-capable'/>
    
      <!--[ Favicon ]-->
      <link href='/main/apple-icon-120x120.png' rel='apple-touch-icon' sizes='120x120'/>
      <link href='/main/apple-icon-152x152.png' rel='apple-touch-icon' sizes='152x152'/>
      <link href='/main/favicon-32x32.png' rel='icon' sizes='32x32' type='image/png'/>
      <link href='/main/favicon-96x96.png' rel='icon' sizes='96x96' type='image/png'/>
      <link href='/main/favicon-16x16.png' rel='icon' sizes='16x16' type='image/png'/>
      <link href='/main/favicon.ico' rel='icon' type='image/x-icon'/>
      <link href='/main/favicon.ico' rel='shortcut icon' type='image/x-icon'/>
    
      <!--[ Stylesheet ]-->
      <style>/*<![CDATA[*/
    /* Merriweather - Font */ @font-face{font-family: 'Merriweather'; font-style: italic; font-weight: 300; font-display: swap; src: local('Merriweather-LightItalic'), url(https://fonts.gstatic.com/s/merriweather/v22/u-4l0qyriQwlOrhSvowK_l5-eR7lXff4jvw.woff2) format('woff2'), url(https://fonts.gstatic.com/s/merriweather/v22/u-4l0qyriQwlOrhSvowK_l5-eR7lXcf8.woff) format('woff')} @font-face{font-family: 'Merriweather'; font-style: italic; font-weight: 700; font-display: swap; src: url(https://fonts.gstatic.com/s/merriweather/v22/u-4l0qyriQwlOrhSvowK_l5-eR7NWPf4jvw.woff2) format('woff2'), url(https://fonts.gstatic.com/s/merriweather/v22/u-4l0qyriQwlOrhSvowK_l5-eR71Wsf8.woff) format('woff')} @font-face{font-family: 'Merriweather'; font-style: italic; font-weight: 900; font-display: swap; src: url(https://fonts.gstatic.com/s/merriweather/v22/u-4l0qyriQwlOrhSvowK_l5-eR7NWPf4jvw.woff2) format('woff2'), url(https://fonts.gstatic.com/s/merriweather/v22/u-4l0qyriQwlOrhSvowK_l5-eR7NWMf8.woff) format('woff')} @font-face{font-family: 'Merriweather'; font-style: normal; font-weight: 300; font-display: swap; src: url(https://fonts.gstatic.com/s/merriweather/v22/u-4n0qyriQwlOrhSvowK_l521wRZWMf6.woff2) format('woff2'), url(https://fonts.gstatic.com/s/merriweather/v22/u-4n0qyriQwlOrhSvowK_l521wRpXA.woff) format('woff')} @font-face{font-family: 'Merriweather'; font-style: normal; font-weight: 700; font-display: swap; src: url(https://fonts.gstatic.com/s/merriweather/v22/u-4n0qyriQwlOrhSvowK_l52xwNZWMf6.woff2) format('woff2'), url(https://fonts.gstatic.com/s/merriweather/v22/u-4n0qyriQwlOrhSvowK_l52xwNpXA.woff) format('woff')} @font-face{font-family: 'Merriweather'; font-style: normal; font-weight: 900; font-display: swap; src: url(https://fonts.gstatic.com/s/merriweather/v22/u-4n0qyriQwlOrhSvowK_l52_wFZWMf6.woff2) format('woff2'), url(https://fonts.gstatic.com/s/merriweather/v22/u-4n0qyriQwlOrhSvowK_l52_wFpXA.woff) format('woff')}
    /* Content */ body{background:#f1f3f6;color:#1f1f1f;font-family:'Merriweather',serif;font-weight:400;-webkit-tap-highlight-color:transparent;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}body:focus{outline:none !important} .mainCont{margin:0 auto;position:fixed;left:0;top:0;right:0;bottom:0;display:flex;align-items:center;justify-content:center;padding:15px} .noIntPop{position:relative;overflow:hidden;text-align:center;padding:15px;border-radius:30px;background:#f1f3f6;box-shadow:inset 0 0 15px rgba(55, 84, 170, 0), inset 0 0 20px rgba(255, 255, 255, 0), 7px 7px 15px rgba(55, 84, 170, 0.15), -7px -7px 20px white, inset 0px 0px 4px rgba(255, 255, 255, 0.2)} .circle.t{top:-150px;right:-150px} .circle.b{bottom:-150px;left:-150px} .noIntCont{position:relative;z-index:1} .noIntIcon{padding:30px} .noConHead{font-weight:700;font-size:1.3rem} .noConDesc{font-size:16px;line-height:1.4em;padding-top:20px;font-weight:400;opacity:.8} .cta,.relCont{display:flex;justify-content:center;align-items:center} .relCont{padding:30px} .cta{width:66px;height:66px;background:#f1f3f6;outline:none;border:none;border-radius:690px;box-shadow:inset 0 0 15px rgba(55, 84, 170, 0), inset 0 0 20px rgba(255, 255, 255, 0), 7px 7px 15px rgba(55, 84, 170, 0.15), -7px -7px 20px white, inset 0px 0px 4px rgba(255, 255, 255, 0.2);transition:box-shadow 399ms ease-in-out} .cta:hover{box-shadow:inset 7px 7px 15px rgba(55, 84, 170, 0.15), inset -7px -7px 20px white, 0px 0px 4px rgba(255, 255, 255, 0.2)} .icon{content:'';width:25px;height:25px;display:inline-block} .iconB{content:'';width:50px;height:50px;display:inline-block} .icon.reload{background:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%239dabc0' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'><polyline points='23 4 23 10 17 10'/><path d='M20.49 15a9 9 0 1 1-2.12-9.36L23 10'/></svg>") center / 25px no-repeat} .iconB.wifiOff{background:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%231f1f1f' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><line x1='1' y1='1' x2='23' y2='23'/><path d='M16.72 11.06A10.94 10.94 0 0 1 19 12.55'/><path d='M5 12.55a10.94 10.94 0 0 1 5.17-2.39'/><path d='M10.71 5.05A16 16 0 0 1 22.58 9'/><path d='M1.42 9a15.91 15.91 0 0 1 4.7-2.88'/><path d='M8.53 16.11a6 6 0 0 1 6.95 0'/><line x1='12' y1='20' x2='12.01' y2='20'/></svg>") center / 50px no-repeat} .circle{position:absolute;z-index:1;width:280px;height:280px;border-radius:50%;background-color:#f1f3f6;box-shadow:inset 8px 8px 12px #d1d9e6, inset -8px -8px 12px #f9f9f9}
      /*]]>*/</style>
    </head>
    
    <body>
      <div class='mainCont notranslate'>
        <div class='noIntPop'>
          <div class='circle t'></div>
          <div class='circle b'></div>
          <div class='noIntCont'>
            <div class='noIntIcon'>
              <i class='iconB wifiOff'></i>
            </div>
            <div class='noConHead'>Oops, You're Offline!</div>
            <div class='noConDesc'>It looks like your network connection isn't working right now.</div>
    
            <div class='relCont'>
              <button class='cta' onclick='window.location.reload()'>
                <i class='icon reload'></i>
              </button>
            </div>
          </div>
        </div>
      </div>
    </body>
    
    </html>`
    
    async function handleRequest(request) {
      return new Response(html, {
        headers: {
          "content-type": "text/html;charset=UTF-8",
        },
      })
    }
    
    addEventListener("fetch", event => {
      return event.respondWith(handleRequest(event.request))
    })
    

    Bagian yang saya tandai bisa Anda ubah

  4. Jika sudah silahkan klik Save anda Deploy

Membuat Routes

  1. Sekarang kembali pada tab Workers
  2. Silahkan klik tombol Add route
  3. Kemudian isi kolom sesuai tabel berikut :
  4. Sesuaikan URL dan nama workers degan milik Anda

    Route Service Environment
    www.kuymase.com/main/* main-kuymase production
    www.kuymase.com/manifest.json manifest-kuymase production
    www.kuymase.com/sw.js serviceworker-kuymase production
    www.kuymase.com/offline.html offline-kuymase production

  5. Berikutnya silahkan lakukan pengetesan masing - masing url, misalnya :
  6. www.kuymase.com/main/android-icon-512x512.png
    www.kuymase.com/manifest.json
    www.kuymase.com/sw.js
    www.kuymase.com/offline.html

Jika semua URL bisa dibuka tanpa error, maka konfigurasi pada Cloudflare sudah selesai.

Edit Template Blog

Langkah berikutnya adalah memasang beberapa kode htnl dan javascript pada blog Anda. Silahkan ikuti langkah berikut

  1. Buka Dashboard blogger Anda
  2. Pilih Tema kemudian Edit HTML
  3. Paste kode dibawah ini di dalam tag <head> atau &lt;head&gt; dan hapus jika ada kode yang sama.
<link href='/main/apple-icon-57x57.png' rel='apple-touch-icon' sizes='57x57'/>
<link href='/main/apple-icon-60x60.png' rel='apple-touch-icon' sizes='60x60'/>
<link href='/main/apple-icon-72x72.png' rel='apple-touch-icon' sizes='72x72'/>
<link href='/main/apple-icon-76x76.png' rel='apple-touch-icon' sizes='76x76'/>
<link href='/main/apple-icon-114x114.png' rel='apple-touch-icon' sizes='114x114'/>
<link href='/main/apple-icon-120x120.png' rel='apple-touch-icon' sizes='120x120'/>
<link href='/main/apple-icon-114x114.png' rel='apple-touch-icon' sizes='144x144'/>
<link href='/main/apple-icon-152x152.png' rel='apple-touch-icon' sizes='152x152'/>
<link href='/main/apple-icon-180x180.png' rel='apple-touch-icon' sizes='180x180'/>
<link href='/main/android-icon-192x192.png' rel='icon' sizes='192x192' type='image/png'/>
<link href='/main/favicon-32x32.png' rel='icon' sizes='32x32' type='image/png'/>
<link href='/main/favicon-96x96.png' rel='icon' sizes='96x96' type='image/png'/>
<link href='/main/favicon-16x16.png' rel='icon' sizes='16x16' type='image/png'/>
<link href='/main/favicon.ico' rel='icon' type='image/x-icon'/>
<meta content='#2196f3' name='msapplication-TileColor'/>
<meta content='/main/ms-icon-144x144.png' name='msapplication-TileImage'/>
<meta content='#2196f3' name='theme-color'/>
<link href='/manifest.json' rel='manifest'/>

Bagian color silahkan samakan dengan yang sebelumnya Anda pilih pada manifest.json

Selanjutnya ikuti langkah dibawah ini sesuai dengan jenis template yang Anda gunakan.

Khusus AMP Template

Jika Anda menggunakan temlate AMP silahkan ikuti langkah berikut :

  1. Tambahkan Serviceworker JS khusus AMP dibawah ini didalam tag <head> atau &lt;head&gt;.
  2. <script async='async' custom-element='amp-install-serviceworker' src='https://cdn.ampproject.org/v0/amp-install-serviceworker-0.1.js'/>
    
  3. Kemudian kode dibawah ini tepat sebelum tag </body> atau &lt;/body&gt;.
  4. <amp-install-serviceworker data-iframe-src='/offline.html' layout='nodisplay' src='/sw.js'/>
    
  5. Kemudian Simpan
Template Standar Non-AMP

Jika Anda menggunakan template standar atau Non-AMP ikuti langkah berikut :

  1. Copy kode berikut dan paste tepat sebelum tag </body> atau &lt;/body&gt;.
  2. <script>/*<![CDATA[*/ /* Service Worker */ if('serviceWorker' in navigator){window.addEventListener('load',()=>{navigator.serviceWorker.register('/sw.js').then(registration=>{console.log('ServiceWorker registeration successful')}).catch(registrationError=>{console.log('ServiceWorker registration failed: ', registrationError)})})}; /*]]>*/</script>
    
  3. Jika sudah silahkan Simpan

Kesimpulan

Sekian cara membuat PWA (Progressive Web App) blogger. Semoga tutorial diatas bermanfaat untuk Anda Jika ada pertanyaan silahkan tinggalkan komentar.


Original Post :
https://www.fineshopdesign.com/2022/03/how-to-build-progressive-web-app.html

Baca Juga

Ad

hello world('print')

2 komentar

  1. dibagian main/* nya kok error ya mas? saya coba klik full url untuk icon androidnya tp gak ada
    1. main/* itu yang nanti teruhubung ke repository Github mas. Coba dicek lagi url dari repo github harus sesuai sma yang dipasang di worker.
Tuliskan pertanyaan seputar topik artikel diatas! Atau pertanyaan out of topic di .
Use this tools to convert basic comment to html characters, then copy it to comment form


image quote pre code

Ad