ITSkillsCenter
Développement Web

Progressive Web Apps : applications web installables

6 دقائق للقراءة
Progressive Web Apps : applications web installables

Ce que vous saurez faire à la fin

  1. Comprendre les avantages PWA vs site classique vs app native.
  2. Configurer un Service Worker pour cache et offline.
  3. Créer un manifeste PWA installable.
  4. Activer push notifications, géolocalisation, caméra.
  5. Tester, déployer et publier sur Play Store via Bubblewrap.

Durée : 2-4 semaines pour PWA complète. Pré-requis : site web HTTPS obligatoire, code source accessible (HTML/CSS/JS ou framework React/Vue/Angular), notions JavaScript, Chrome DevTools, hébergement supportant les Service Workers.

Étape 1 — Comprendre les avantages PWA

Critère Site classique PWA App native
Installation Non Icône écran d’accueil Store
Offline Non Oui (Service Worker) Oui
Push notifications Limité Oui Oui
Coût dev 1,2× 3-5×
App Store Non Oui (TWA) Oui
Mises à jour Auto Auto Via Store
Performance Standard Élevée Maximum

Idéal pour : e-commerce, médias, SaaS B2B, services locaux. Limité pour : jeux 3D, app caméra avancée.

Étape 2 — Vérifier les prérequis techniques

Checklist obligatoire :
✓ HTTPS activé (Let's Encrypt gratuit)
✓ Service Worker enregistrable
✓ Web App Manifest présent
✓ Icônes 192x192 et 512x512 minimum
✓ Responsive design (mobile-first)
✓ Performance Lighthouse > 80

Test : Chrome DevTools → Application → Manifest + Service Workers

Étape 3 — Créer le Web App Manifest

Fichier manifest.json à la racine du site :

{
  "name": "Mon App PWA",
  "short_name": "MonApp",
  "description": "Description courte de l'application",
  "start_url": "/",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#2563EB",
  "orientation": "portrait",
  "scope": "/",
  "lang": "fr-SN",
  "icons": [
    {"src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any maskable"},
    {"src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any maskable"}
  ],
  "shortcuts": [
    {"name": "Mon profil", "url": "/profile", "icons": [{"src": "/icons/profile.png", "sizes": "96x96"}]}
  ]
}

Étape 4 — Lier le manifeste dans HTML

<!-- Dans <head> -->
<link rel="manifest" href="/manifest.json">
<meta name="theme-color" content="#2563EB">
<link rel="apple-touch-icon" href="/icons/icon-192.png">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="apple-mobile-web-app-title" content="MonApp">

Étape 5 — Créer un Service Worker basique

Fichier service-worker.js à la racine :

const CACHE_NAME = 'monapp-v1';
const ASSETS = [
  '/',
  '/styles.css',
  '/app.js',
  '/icons/icon-192.png',
  '/offline.html'
];

self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME).then((cache) => cache.addAll(ASSETS))
  );
  self.skipWaiting();
});

self.addEventListener('activate', (event) => {
  event.waitUntil(
    caches.keys().then((keys) =>
      Promise.all(keys.filter(k => k !== CACHE_NAME).map(k => caches.delete(k)))
    )
  );
  self.clients.claim();
});

self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request).then((response) =>
      response || fetch(event.request).catch(() => caches.match('/offline.html'))
    )
  );
});

Étape 6 — Enregistrer le Service Worker

// Dans app.js ou main.js
if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/service-worker.js')
      .then((reg) => console.log('SW registered:', reg.scope))
      .catch((err) => console.error('SW registration failed:', err));
  });
}

Étape 7 — Implémenter une stratégie de cache avancée

3 stratégies courantes :

// 1. Cache First (pour assets statiques)
self.addEventListener('fetch', (event) => {
  if (event.request.url.includes('/static/')) {
    event.respondWith(
      caches.match(event.request).then(r => r || fetch(event.request))
    );
  }
});

// 2. Network First (pour API)
async function networkFirst(request) {
  try {
    const response = await fetch(request);
    const cache = await caches.open('api-cache');
    cache.put(request, response.clone());
    return response;
  } catch {
    return caches.match(request);
  }
}

// 3. Stale While Revalidate (pour images dynamiques)
async function staleWhileRevalidate(request) {
  const cache = await caches.open('img-cache');
  const cached = await cache.match(request);
  const fetched = fetch(request).then(r => { cache.put(request, r.clone()); return r; });
  return cached || fetched;
}

Étape 8 — Activer le bouton « Installer l’app »

let deferredPrompt;

window.addEventListener('beforeinstallprompt', (e) => {
  e.preventDefault();
  deferredPrompt = e;
  // Afficher votre bouton "Installer l'app"
  document.getElementById('install-btn').style.display = 'block';
});

document.getElementById('install-btn').addEventListener('click', async () => {
  if (!deferredPrompt) return;
  deferredPrompt.prompt();
  const { outcome } = await deferredPrompt.userChoice;
  console.log(outcome === 'accepted' ? 'Installé' : 'Refusé');
  deferredPrompt = null;
});

Étape 9 — Activer les Push Notifications

// Demander permission
async function subscribeUser() {
  const permission = await Notification.requestPermission();
  if (permission !== 'granted') return;

  const reg = await navigator.serviceWorker.ready;
  const sub = await reg.pushManager.subscribe({
    userVisibleOnly: true,
    applicationServerKey: 'YOUR_VAPID_PUBLIC_KEY'
  });

  // Envoyer la subscription au serveur
  await fetch('/api/save-subscription', {
    method: 'POST',
    body: JSON.stringify(sub),
    headers: {'Content-Type': 'application/json'}
  });
}

// Recevoir notification dans Service Worker
self.addEventListener('push', (event) => {
  const data = event.data.json();
  event.waitUntil(
    self.registration.showNotification(data.title, {
      body: data.body,
      icon: '/icons/icon-192.png',
      badge: '/icons/badge.png',
      data: { url: data.url }
    })
  );
});

self.addEventListener('notificationclick', (event) => {
  event.notification.close();
  event.waitUntil(clients.openWindow(event.notification.data.url));
});

Étape 10 — Accéder caméra, géolocalisation, partage

// Géolocalisation
navigator.geolocation.getCurrentPosition(
  (pos) => console.log(pos.coords),
  (err) => console.error(err)
);

// Caméra
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
document.querySelector('video').srcObject = stream;

// Web Share API (partage natif)
if (navigator.share) {
  await navigator.share({
    title: 'Mon contenu',
    url: 'https://monsite.com/article'
  });
}

// Notifications badging
if ('setAppBadge' in navigator) {
  navigator.setAppBadge(5); // afficher badge "5" sur icône
}

Étape 11 — Auditer avec Lighthouse

Chrome DevTools > Lighthouse > cochez « Progressive Web App » > Run audit. Cibles :

  • Performance > 90
  • Accessibility > 90
  • Best Practices > 90
  • SEO > 90
  • PWA : tous les critères validés (badge « Installable »)

Étape 12 — Publier sur Google Play Store via Bubblewrap

# Installation
npm install -g @bubblewrap/cli

# Initialisation projet (génère un projet Android Studio)
bubblewrap init --manifest=https://votresite.com/manifest.json

# Build APK / AAB
bubblewrap build

# Créez compte Google Play Developer (25 USD one-time)
# Uploadez l'AAB généré
# Décrivez votre app, screenshots, description
# Publication 2-7 jours pour validation

Avantages : votre PWA dans le Play Store, présence officielle, install via Play Store en 1 clic.

Étape 13 — Mesurer l’usage PWA

Google Analytics 4 events à tracker :

// Quand l'app est installée
window.addEventListener('appinstalled', () => {
  gtag('event', 'pwa_installed');
});

// Quand l'app est lancée en mode standalone
if (window.matchMedia('(display-mode: standalone)').matches) {
  gtag('event', 'pwa_launch');
}

// Push notification cliquée
self.addEventListener('notificationclick', () => {
  gtag('event', 'push_clicked');
});

Étape 14 — Maintenir et faire évoluer

Routine mensuelle :

  • Audit Lighthouse (régression performance)
  • Vérifier compatibilité nouveaux navigateurs (Safari iOS notamment, support PWA limité)
  • Update Service Worker (versionner CACHE_NAME pour forcer refresh)
  • Surveiller les KPI : install rate, launch rate, NPS push notifications
  • Itérer sur les features les plus utilisées

Erreurs classiques en PWA

  • Pas de HTTPS : Service Worker refuse de s’enregistrer.
  • Service Worker mal versionné : users coincés sur ancienne version.
  • Cache trop agressif : nouveautés invisibles, frustration.
  • Push notifications spam : users désabonnent, marque ternie.
  • iOS limited support : testez tout sur Safari iOS, surprises possibles.

Checklist PWA complète

✓ HTTPS actif
✓ Manifest JSON complet et lié
✓ Icônes 192 et 512 (any maskable)
✓ Service Worker enregistré
✓ Stratégie de cache (CF, NF, SWR) selon ressource
✓ Page offline.html fallback
✓ Bouton "Installer l'app" actif
✓ Push notifications avec VAPID + backend
✓ Web Share / Camera / Geo si pertinent
✓ Lighthouse PWA score 100
✓ Publié sur Play Store via Bubblewrap
✓ Analytics PWA events trackés
✓ Test régulier Safari iOS
Besoin d'un site web ?

Confiez-nous la Création de Votre Site Web

Site vitrine, e-commerce ou application web — nous transformons votre vision en réalité digitale. Accompagnement personnalisé de A à Z.

À partir de 250.000 FCFA
Parlons de Votre Projet
Publicité