Sehemu A: Hakikisha PWA iko tayari (kabla ya PWABuilder)
A1) PWA lazima iwe kwenye HTTPS

https://faulink.com

localhost pia inakubali wakati wa dev.

A2) Hakikisha una hizi files 3

manifest.json (au manifest.webmanifest)

sw.js (service worker)

offline.html (ukurasa wa offline)

Sehemu B: Code za msingi za PWA (weka/rekebisha)
B1) manifest.json (toleo la PRO kwa Faulink)

Weka kwenye root: /manifest.json

{
"name": "Faulink Systems",
"short_name": "Faulink",
"id": "/",
"start_url": "/index.php?source=pwa",
"scope": "/",
"display": "standalone",
"orientation": "portrait",
"background_color": "#0b1220",
"theme_color": "#0066cc",
"lang": "sw",
"description": "Faulink ni mfumo wa kisasa wa kidigitali kwa shule na biashara: usimamizi wa taarifa, malipo, ripoti na ufuatiliaji kwa urahisi na usalama.",
"icons": [
{ "src": "/assets/icons/icon-192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "/assets/icons/icon-512.png", "sizes": "512x512", "type": "image/png" },
{ "src": "/assets/icons/maskable-512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
]
}

✅ Muhimu:

id iwe simple path (mf. "/"). Usiiweke kama string yenye JSON ndani.

start_url weka na ?source=pwa (optional) ili ujue watumiaji wa PWA.

icons: angalau 192 na 512.

B2) sw.js (service worker ya production)

Weka kwenye root: /sw.js

'use strict';

const SW_VERSION = 'v1.0.0';
const CACHE_STATIC = `static-${SW_VERSION}`;
const CACHE_PAGES = `pages-${SW_VERSION}`;
const CACHE_ASSETS = `assets-${SW_VERSION}`;

const OFFLINE_URL = '/offline.html';

const STATIC_ASSETS = [
OFFLINE_URL,
'/manifest.json'
];

self.addEventListener('install', (event) => {
event.waitUntil((async () => {
const cache = await caches.open(CACHE_STATIC);
await cache.addAll(STATIC_ASSETS);
self.skipWaiting();
})());
});

self.addEventListener('activate', (event) => {
event.waitUntil((async () => {
const keys = await caches.keys();
await Promise.all(
keys
.filter((k) => ![CACHE_STATIC, CACHE_PAGES, CACHE_ASSETS].includes(k))
.map((k) => caches.delete(k))
);
await self.clients.claim();
})());
});

const isNavigationRequest = (req) =>
req.mode === 'navigate' ||
(req.method === 'GET' && (req.headers.get('accept') || '').includes('text/html'));

async function cacheFirst(req) {
const cache = await caches.open(CACHE_ASSETS);
const cached = await cache.match(req);
if (cached) return cached;

const res = await fetch(req);
if (res && res.ok) cache.put(req, res.clone());
return res;
}

async function networkFirst(req) {
const cache = await caches.open(CACHE_PAGES);
try {
const res = await fetch(req);
if (res && res.ok) cache.put(req, res.clone());
return res;
} catch {
const cached = await cache.match(req);
return cached || caches.match(OFFLINE_URL);
}
}

self.addEventListener('fetch', (event) => {
const req = event.request;
if (req.method !== 'GET') return;

const url = new URL(req.url);
if (url.origin !== self.location.origin) return;

if (isNavigationRequest(req)) {
event.respondWith(networkFirst(req));
return;
}

const isAsset =
url.pathname.startsWith('/assets/') ||
['.css', '.js', '.png', '.jpg', '.jpeg', '.webp', '.svg', '.ico', '.woff', '.woff2', '.ttf']
.some(ext => url.pathname.endsWith(ext));

if (isAsset) {
event.respondWith(cacheFirst(req));
return;
}

event.respondWith((async () => {
const cached = await caches.match(req);
try {
return await fetch(req);
} catch {
return cached || caches.match(OFFLINE_URL);
}
})());
});

self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') self.skipWaiting();
});

✅ Ukitoa update baadaye, badilisha SW_VERSION (mf. v1.0.1) ili cache ibadilike.

B3) offline.html

Weka: /offline.html

<!doctype html>
<html lang="sw">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<meta name="theme-color" content="#0b1220" />
<title>Upo Offline</title>
<style>
body{font-family:system-ui,Segoe UI,Roboto,Arial;margin:0;background:#0b1220;color:#fff}
.wrap{min-height:100vh;display:flex;align-items:center;justify-content:center;padding:24px}
.card{max-width:560px;width:100%;background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.15);
border-radius:16px;padding:22px;box-shadow:0 10px 30px rgba(0,0,0,.35)}
h1{margin:0 0 10px;font-size:22px}
p{margin:0 0 16px;opacity:.9;line-height:1.5}
button{border:0;background:#fff;color:#0b1220;padding:10px 14px;border-radius:12px;cursor:pointer;font-weight:700}
</style>
</head>
<body>
<div class="wrap">
<div class="card">
<h1>Upo Offline</h1>
<p>Hakuna intaneti kwa sasa. Rudia tena baada ya mtandao kurudi.</p>
<button onclick="location.reload()">Jaribu Tena</button>
</div>
</div>
</body>
</html>
B4) Code ya kuweka kwenye index.php (head + register SW)

Ndani ya <head> weka:

<link rel="manifest" href="/manifest.json">
<meta name="theme-color" content="#0066cc">

<link rel="apple-touch-icon" href="/assets/icons/icon-192.png">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-title" content="Faulink">

Kabla ya </body> weka SW registration:

<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', async () => {
try {
const reg = await navigator.serviceWorker.register('/sw.js', { scope: '/' });

// auto refresh on update (optional)
reg.addEventListener('updatefound', () => {
const w = reg.installing;
if (!w) return;
w.addEventListener('statechange', () => {
if (w.state === 'installed' && navigator.serviceWorker.controller) {
w.postMessage({ type: 'SKIP_WAITING' });
}
});
});

navigator.serviceWorker.addEventListener('controllerchange', () => {
window.location.reload();
});
} catch (e) {
console.warn('SW failed:', e);
}
});
}
</script>
Sehemu C: Ku-test kabla ya PWABuilder

Fungua hizi kwenye browser:

https://faulink.com/manifest.json

lazima ionyeshe JSON, isiwe 404

https://faulink.com/sw.js

https://faulink.com/offline.html

https://faulink.com/assets/icons/icon-512.png

Kama moja inakataa, PWABuilder atakwama.

Sehemu D: PWABuilder → kupata Android App Bundle (AAB)
D1) Ingia PWABuilder

Nenda PWABuilder

Weka URL ya site yako (au direct manifest URL)

Inafanya “analysis”

D2) Rekebisha Action Items

Kama kuna warnings, hakikisha:

description ipo

id ipo na ni sahihi (mf. "/")

orientation ipo

D3) Store ready

Ukiona “Awesome! Your PWA is store ready!”

Bonyeza Android → Generate Package

D4) Jaza details za Android package

Itakuuliza:

Application Id / Package name (mf. com.faulink.systems)

App name

Version

Kisha itatengeneza package na utapata Download .aab.

✅ File muhimu kwa Play Store ni .aab.

Sehemu E: Google Play Console (ku-upload AAB mpaka live)
E1) Fungua Play Console

Unda account (kuna fee ya mara moja)

Create app

Chagua:

App type: App

Free or Paid: chagua unavyotaka

Default language: Swahili/English

E2) Upload AAB

Nenda:

Release → Production → Create new release

Upload .aab

Save

E3) Store Listing (lazima)

Andaa:

App name

Short description

Full description

App icon 512×512

Feature graphic 1024×500

Screenshots (2–8 recommended; 1080×1920 ni standard)

E4) Privacy Policy (lazima)

Unda page kwenye site yako mfano:

https://faulink.com/privacy-policy

Kisha uweke link hiyo Play Console.

E5) App Content / Data Safety (lazima)

Utaulizwa:

App inakusanya data gani (mf. email, name, etc.)

Inatumika vipi

Kama data inasharekwa

Jibu kweli kulingana na mfumo wako.

E6) Submit for review

Ukishajaza kila kitu, “Submit” — kisha subiri review.

Sehemu F: Model yako ya biashara (2 days trial + manual activation) iwe SAFE kwa Play Store

Wewe unafanya:

Trial → lock → user anakulipa nje → wewe unamactivate kupitia website ✅

Ili isiwe issue kwenye review:

Usionyeshe “Lipia hapa M-Pesa” ndani ya app kama “digital unlock button”

Badala yake, onyesha message ya support:

Message ya kuonyesha trial ikisha (mfano)

“Trial imeisha. Tafadhali wasiliana na Faulink kwa ajili ya ku-activate huduma.”

Hii ni clean.

Sehemu G: “Kitu kinachowachanganya watu” (common mistakes)

Manifest link kwenye head haifanani na file uliyoupdate

hakikisha: <link rel="manifest" href="/manifest.json">

Cache ya browser/service worker

badilisha SW_VERSION kila update + Ctrl+F5

Icon 512 haipatikani (404)

start_url inaredirect loop

Sehemu H: Vitu utakavyohitaji kwa Play Store (quick checklist)

✅ .aab kutoka PWABuilder

✅ Privacy Policy URL

✅ Screenshots

✅ Feature graphic 1024×500

✅ Description

✅ App category (Education/Business)

✅ Data safety answers

Ukiwa tayari, niambie vitu 2 tu: