Follow us on Youtube.  | Follow Website !

How to build an app? Tutorial

Table of contents

In this article, I will guide you to build a PWA (Progressive Web app) for your website in an easy way.

So, before you start the process to set up PWA For your website, you must know what is PWA and why it is important for your website.

What is PWA (Progressive Web App)

Progressive web apps are a new way to create native-like experiences on the web. They combine the best of both worlds, providing users with the best of a website and an app.

A Progressive Web App is a webview app that can be installed on your phone or tablet like an app, but it's built with web technologies. This means you can add features like push notifications and offline support without having to build a separate native app.

Why is PWA Important?

A Progressive Web App is a website that behaves like an app on the user’s device. It loads quickly, is responsive to different devices, and can be accessed at any time without the need to download anything.

A Progressive Web App offers a better experience than a traditional website for both users and developers. It has features that are usually found in native apps such as push notifications, offline support, and home screen icons. This means that it will load faster, look better on all types of screens, have more functionality than a regular website, and will be available even when the device is offline.

How to build a PWA for Blogger

In order to build a Progressive Web App, you will need to add some features to your website. These features include service workers, which allow your site to work offline, and push notifications for when users return to your site. You can also install an Add-to-Home screen prompt on your website that prompts users to add your site or app to their home screen on their mobile device or desktop computer.

This tutorial may be a bit difficult to understand, but if you follow all the steps correctly, you will surely be able to build a PWA for your Blogger Website.

Full process

1) Prepare an icon for your website in .png extension.
2) Go to https://www.favicon-generator.org/ and upload your icon.
3) Download the favicon zip and extract all the icons,.
4) The total files inside the zip would be exactly 25. Including your icon, it would be 26.
5) Delete unnecessary files like 
manifest.json
browserconfig.xml

6) Login to Github and create a repository  icon-blogname i.e icon-fineshop and upload your icons. Prepare 5 screenshots of your app and name them as:

scr1.png
scr2.png
scr3.png
scr4.png
scr5.png


7) Total files uploaded is (26+5)=31 files.



8) Login in to cloudflare.com  and connect your site.
9) Create 4 workers in cloudflare and name them as:

We will create 4 workers in Cloudflare, to make it easier to create a route later, I will suggest you to save it with the worker name and blog name,

for example:
main-fineshop
manifest-fineshop
serviceworker-fineshop
offline-fineshop

10) Go to Workers section and click on Manage Workers.  Click create a service and make these four services. Remember the names of those services are given at Step 12. Rename the fineshop to your blogname.

11) Click on Quick edit and replace the existing code with fhe given code.

Main worker

addEventListener("fetch", event => {
event.respondWith(handleRequest(event))
})

//const BUCKET_NAME = "main"
const BUCKET_URL = `https://cdn.statically.io/gh/kumardeo080/icon-fineshop`

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 })
}
}

Manifest.json

addEventListener("fetch", event => {
const data = {
name: "Fineshop Design",
short_name: "Fineshop Design",
description: "Install Now Fineshop Design - Let's fuel creativity",
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"
},
{
src: "\/main\/android-icon-192x192.png",
sizes: "192x192",
type: "image\/png"
},
{
src: "\/main\/apple-icon-144x144.png",
sizes: "144x144",
type: "image\/png"
},
{
src: "\/main\/android-icon-96x96.png",
sizes: "96x96",
type: "image\/png"
},
{
src: "\/main\/android-icon-72x72.png",
sizes: "72x72",
type: "image\/png"
},
{
src: "\/main\/android-icon-48x48.png",
sizes: "48x48",
type: "image\/png"
},
{
src: "\/main\/android-icon-36x36.png",
sizes: "36x36",
type: "image\/png"
}
],
shortcuts: [
{
name: "Fineshop Design",
short_name: "Fineshop Design",
description: "The Best Website where you can find Blogger Widgets, Tech News, Tech Reviews, Coding related Tutorials and many more.",
url: "\/?utm_source=homescreen",
icons: [
{
src: "\/main\/android-icon-192x192.png",
sizes: "192x192"
}
]
},
{
name: "Fineshop Design - Blog",
short_name: "Fineshop Design - Blog",
description: "Explore Fineshop Design Blog.",
url: "\/search?utm_source=homescreen",
icons: [
{
src: "\/main\/android-icon-192x192.png",
sizes: "192x192"
}
]
},
{
name: "Blogger Widgets",
short_name: "Blogger Widgets",
description: "Useful Widgets for your Blog.",
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"
}
})
)
})

Service Worker

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))
})

Offline

        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))

})

7) Rename each workers file as:
1) main-blogname i.e main-fineshop
3) serviceworker-blogname i.e serviceworker-fineshop
4) offline-blogname i.e offline-fineshop

Route Service
www.fineshopdesign.com/main/* main-fineshop
www.fineshopdesign.com/manifest.json manifest-fineshop
www.fineshopdesign.com/sw.jsserviceworker-fineshop
www.fineshopdesign.com/offline.htmloffline-fineshop

Set all environment to production.

2) Try to open these links as:


Replace the fineshopdesign in both route and links with your url and blogname.

Blogger Dashboard

1) Now go to your blogger Dashboard and you need to add two codes extra.
2)  Click on edit html and add this code below <head>.
3) Put these below code: 
<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'/>

5) Add the following Javascript code above to </body>
      <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>  

Post a Comment