2022-10-16 08:45 Temps de lecture : 25 min

Comment convertir WebApp en PWA avec notification push

Transformer une application web en PWA avec notifications push

Cet article explore le processus de conversion d'une application web ou d'un site web en une Progressive Web App (PWA), en y intégrant des notifications push grâce à Firebase Cloud Messaging.

Dans l'environnement numérique actuel, la migration vers les PWA est une tendance majeure. Ces applications offrent des avantages significatifs tels que le fonctionnement hors ligne, les notifications push et la synchronisation en arrière-plan. Ces atouts transforment une simple application web en une expérience utilisateur comparable à celle d'une application native.

De grandes marques comme Twitter et Amazon ont déjà adopté les PWA, témoignant de leur efficacité pour améliorer l'engagement des utilisateurs.

Qu'est-ce qu'une PWA ?

Une PWA est l'union d'une application web standard (HTML, CSS, JS) avec des fonctionnalités empruntées aux applications natives. Elle conserve le comportement d'une application web classique sur tous les navigateurs modernes tout en y ajoutant des capacités avancées. Cette approche améliore la performance de l'application, la rend plus évolutive, et réduit la charge sur le serveur grâce à la pré-récupération et la mise en cache des éléments.

Différences entre PWA et application web classique

Aspect Application Web Classique Progressive Web App (PWA)
Installable Non Oui, comme une application native
Fonctionnalités Fonctionnalités limitées Fonctionnalités natives supplémentaires
Expérience utilisateur Moins immersive Similaire à une application native, une fois installée
Accessibilité Nécessite la saisie d'une adresse web Accessible en un clic après installation
Mise en cache Mise en cache HTTP (navigateur uniquement) Mise en cache côté client (plus flexible)
Publication Pas sur les stores Publication possible sur les stores (Google Play Store et IOS App Store)

Adopter les PWA améliore significativement la puissance et la portée de votre application.

Pourquoi les entreprises devraient opter pour les PWA ?

Face à la demande de développement d'applications web, puis d'applications Android et iOS, les PWA représentent une alternative économique et rapide. Le développement de la même fonctionnalité pour chaque plateforme génère des coûts supplémentaires et prolonge le délai de mise sur le marché.

Les PWA répondent souvent aux exigences des clients avec un budget limité ou une contrainte de délai. Dans ce cas, les PWA sont une solution idéale, pouvant être transformée en application Android via TWA (Trusted Web Activity) pour une publication sur le Play Store.

Si des fonctionnalités spécifiques ne sont pas prises en charge par les PWA, le développement natif reste pertinent. Cependant, une PWA peut être déployée temporairement sur le Play Store en attendant la fin du développement natif. C'est l'approche qu'a suivie Titan Eyeplus, qui a initialement lancé une PWA via TWA avant de déployer son application Android.

Fonctionnalités des PWA

Les PWA ajoutent des caractéristiques d'applications natives à vos applications web :

  • Installation : Votre application web s'installe comme une application native.
  • Mise en cache : La mise en cache permet un fonctionnement hors ligne.
  • Notifications push : Engagez vos utilisateurs avec des notifications envoyées depuis votre serveur.
  • Géolocalisation : L'application peut réagir aux changements de localisation de l'appareil.
  • Paiement : Facilitez les transactions au sein de l'application.

D'autres fonctionnalités incluent :

  • Raccourcis : Accès rapide à des URLs via le fichier manifeste.
  • API de partage web : Réception de données partagées par d'autres applications.
  • API Badge : Affichage du nombre de notifications.
  • API de synchronisation en arrière-plan : Synchronisation des données hors ligne.
  • Sélecteur de contacts/fichiers : Accès aux contacts et fichiers de l'utilisateur.

Avantages des PWA par rapport aux applications natives

  • Compatibilité multiplateforme (Android, iOS, Desktop).
  • Coûts de développement réduits.
  • Déploiement simplifié.
  • Meilleure visibilité grâce au référencement.
  • Sécurité renforcée (HTTPS).

Inconvénients des PWA par rapport aux applications natives

  • Fonctionnalités limitées.
  • Support variable selon les appareils.
  • Image de marque moins forte (pas dans les stores).

Vous pouvez augmenter la visibilité de votre PWA en la déployant comme une application Android via TWA.

Éléments nécessaires pour la conversion en PWA

  • Service Worker : Essentiel pour la mise en cache, les notifications push et la gestion des requêtes.
  • Fichier manifeste : Contient les informations nécessaires à l'installation et à l'affichage de l'application.
  • Logo : Image de haute qualité (512x512px) pour l'icône de l'application.
  • Conception responsive : L'application doit s'adapter à différentes tailles d'écran.

Qu'est-ce qu'un Service Worker ?

Un Service Worker est un script côté client qui agit comme un proxy entre votre application et le monde extérieur. Il offre des services tels que les notifications push et la mise en cache. Il s'exécute indépendamment du script principal, n'a pas accès au DOM et communique avec le thread principal via des messages. Il peut accéder à IndexedDB, Fetch API, et CacheStorage API.

Fonctions clés du Service Worker :

  • Interception des requêtes HTTP.
  • Réception de notifications push.
  • Support hors ligne de l'application.

Le domaine d'origine doit être en HTTPS pour éviter les attaques.

Le fichier Manifest

Le fichier manifest.json contient les détails de votre application PWA pour le navigateur :

  • nom : Nom de l'application.
  • short_name : Nom abrégé.
  • description : Description de l'application.
  • start_url : Page d'accueil au lancement.
  • icons : Ensemble d'images.
  • background_color : Couleur d'arrière-plan de l'écran de démarrage.
  • display : Personnalisation de l'interface utilisateur du navigateur.
  • theme_color : Couleur du thème de l'application.
  • scope : Portée de l'URL de l'application.
  • shortcuts : Liens rapides vers l'application.

Conversion d'une application web en PWA

Pour l'exemple, une structure de dossiers avec des fichiers statiques est utilisée :

  • index.html (page d'accueil)
  • dossiers articles, auteurs, outils et offres (avec leurs index.html respectifs)

Les étapes suivantes permettent de convertir un site ou une application web en PWA :

Création des images pour PWA

Le logo doit être recadré dans un rapport 1:1 en plusieurs tailles. L'outil https://tools.crawlink.com/tools/pwa-icon-generator/ peut être utilisé à cette fin.

Création du fichier manifeste

Le fichier manifest.json contient les informations de l'application.

{
	"name": "toptips.fr",
	"short_name": "toptips.fr",
	"description": "toptips.fr produit des articles de haute qualité en technologie et finance, développe des outils et des APIs pour aider les entreprises et les particuliers à se développer.",
	"start_url": "/",
	"icons": [{
		"src": "assets/icon/icon-128x128.png",
		"sizes": "128x128",
		"type": "image/png"
	}, {
		"src": "assets/icon/icon-152x152.png",
		"sizes": "152x152",
		"type": "image/png"
	}, {
		"src": "assets/icon/icon-192x192.png",
		"sizes": "192x192",
		"type": "image/png"
	}, {
		"src": "assets/icon/icon-384x384.png",
		"sizes": "384x384",
		"type": "image/png"
	}, {
		"src": "assets/icon/icon-512x512.png",
		"sizes": "512x512",
		"type": "image/png"
	}],
	"background_color": "#EDF2F4",
	"display": "standalone",
	"theme_color": "#B20422",
	"scope": "/",
	"shortcuts": [{
			"name": "Articles",
			"short_name": "Articles",
			"description": "1595 articles sur la Sécurité, Sysadmin, Marketing Digital, Cloud Computing, Développement, et de nombreux autres sujets.",
			"url": "https://geekflare.com/articles",
			"icons": [{
				"src": "/assets/icon/icon-152x152.png",
				"sizes": "152x152"
			}]
		},
		{
			"name": "Auteurs",
			"short_name": "Auteurs",
			"description": "toptips.fr - Auteurs",
			"url": "/auteurs",
			"icons": [{
				"src": "/assets/icon/icon-152x152.png",
				"sizes": "152x152"
			}]
		},
		{
			"name": "Outils",
			"short_name": "Outils",
			"description": "toptips.fr - Outils",
			"url": "https://toptips.fr.com/tools",
			"icons": [{
				"src": "/assets/icon/icon-152x152.png",
				"sizes": "152x152"
			}]
		},
		{
			"name": "Offres",
			"short_name": "Offres",
			"description": "toptips.fr - Offres",
			"url": "/deals",
			"icons": [{
				"src": "/assets/icon/icon-152x152.png",
				"sizes": "152x152"
			}]
		}
	]
}

Enregistrement du Service Worker

Deux fichiers sont créés : register-service-worker.js et service-worker.js. Le premier s'exécute sur le thread principal et a accès au DOM, tandis que le second s'exécute indépendamment et a une durée de vie courte.

Contenu de register-service-worker.js :

if ('serviceWorker' in navigator) {
    window.addEventListener('load', function() {
        navigator.serviceWorker.register('/service-worker.js');
    });
}

Contenu de service-worker.js :

self.addEventListener('install', (event) => { // event when service worker install
    console.log( 'install', event);
    self.skipWaiting();
});

self.addEventListener('activate', (event) => { // event when service worker activated
    console.log('activate', event);
    return self.clients.claim();
});

self.addEventListener('fetch', function(event) { // HTTP request interceptor
    event.respondWith(fetch(event.request)); // send all http request without any cache logic
    /*event.respondWith(
        caches.match(event.request).then(function(response) {
            return response || fetch(event. request);
        })
    );*/ // cache new request. if already in cache serves with the cache.
});

Pour l'instant, la mise en cache pour le support hors ligne n'est pas abordée, l'accent étant mis sur la conversion de l'application web en PWA.

Ajoutez le fichier manifeste et le script au <head> de votre page HTML :

<link rel="manifest" href="https://toptips.fr.com/manifest.json">
<script src="/register-service-worker.js"></script>

Après actualisation, l'application est installable.

Pour WordPress, des plugins de conversion PWA sont disponibles. Pour VueJS ou ReactJS, la méthode ci-dessus ou des modules PWA npm peuvent être utilisés.

Activation des notifications push

Les notifications push permettent d'engager les utilisateurs. Leur activation repose sur :

  • API de notifications : Pour configurer l'affichage des notifications.
  • API push : Pour recevoir les notifications envoyées par le serveur.

La première étape consiste à vérifier si l'API de notification est disponible et à demander l'autorisation à l'utilisateur. Collez le code suivant dans votre register-service-worker.js :

if ('Notification' in window && Notification.permission != 'granted') {
    console.log('Ask user permission')
    Notification.requestPermission(status => {  
        console.log('Status:'+status)
        displayNotification('Notification Enabled');
    });
}


const displayNotification = notificationTitle => {
    console.log('display notification')
    if (Notification.permission == 'granted') {
        navigator.serviceWorker.getRegistration().then(reg => {
            console.log(reg)
            const options = {
                    body: 'Thanks for allowing push notification !',
                    icon:  '/assets/icons/icon-512x512.png',
                    vibrate: [100, 50, 100],
                    data: {
                      dateOfArrival: Date.now(),
                      primaryKey: 0
                    }
                  };
    
            reg.showNotification(notificationTitle, options);
        });
    }
};

Si l'API de notification est supportée et que l'utilisateur a donné son autorisation, une notification de l'application sera reçue.

Activation de Firebase Cloud Messaging et création d'un abonnement

Firebase Cloud Messaging est utilisé pour envoyer des notifications du serveur à l'utilisateur. Un abonnement est nécessaire pour chaque utilisateur.

  1. Créer un compte Firebase via https://firebase.google.com/.
  2. Créer un nouveau projet.
  3. Aller dans les paramètres du projet, puis Cloud Messaging et générer des clés.

Les trois clés nécessaires sont :

  • Clé du serveur de projet.
  • Clé privée des certificats push web.
  • Clé publique des certificats push web.

Collez le code suivant dans register-service-worker.js:

const updateSubscriptionOnYourServer = subscription => {
    console.log('Write your ajax code here to save the user subscription in your DB', subscription);
    // write your own ajax request method using fetch, jquery, axios to save the subscription in your server for later use.
};

const subscribeUser = async () => {
    const swRegistration = await navigator.serviceWorker.getRegistration();
    const applicationServerPublicKey = 'BOcTIipY07N4Y63Y-9r7NMoJHofmCzn3Pu9g-LMsgIMGH4HVr42_LW9ia0lMr68TsTLKS3UcdkE3IcC52hJDYsY'; // paste your webpush certificate public key
    const applicationServerKey = urlB64ToUint8Array(applicationServerPublicKey);
    swRegistration.pushManager.subscribe({
      userVisibleOnly: true,
      applicationServerKey
    })
    .then((subscription) => {
        console.log('User is subscribed newly:', subscription);
        updateSubscriptionOnServer(subscription);
    })
    .catch((err) => {
        if (Notification.permission === 'denied') {
          console.warn('Permission for notifications was denied')
        } else {
          console.error('Failed to subscribe the user: ', err)
        }
    });
};
const urlB64ToUint8Array = (base64String) => {
    const padding = '='.repeat((4 - base64String.length % 4) % 4)
    const base64 = (base64String + padding)
        .replace(/-/g, '+')
        .replace(/_/g, '/')

    const rawData = window.atob(base64);
    const outputArray = new Uint8Array(rawData.length);

    for (let i = 0; i < rawData.length; ++i) {
        outputArray[i] = rawData.charCodeAt(i);
    }
    return outputArray;
};

const checkSubscription = async () => {
    const swRegistration = await navigator.serviceWorker.getRegistration();
    swRegistration.pushManager.getSubscription()
    .then(subscription => {
        if (!!subscription) {
            console.log('User IS Already subscribed.');
            updateSubscriptionOnYourServer(subscription);
        } else {
            console.log('User is NOT subscribed. Subscribe user newly');
            subscribeUser();
        }
    });
};

checkSubscription();

Collez le code suivant dans service-worker.js :

self.addEventListener('push', (event) => {
  const json = JSON.parse(event.data.text())
  console.log('Push Data', event.data.text())
  self.registration.showNotification(json.header, json.options)
});

L'abonnement permet d'envoyer des notifications push à l'utilisateur jusqu'à ce qu'il refuse ces services.

Envoi de notifications push depuis un backend Node.js

Le module npm web-push simplifie l'envoi de notifications. Voici un exemple :

const webPush = require('web-push');
    // pushSubscription is nothing but subscription that you sent from your front-end to save it in DB
    const pushSubscription = {"endpoint":"https://updates.push.services.mozilla.com/wpush/v2/gAAAAABh2…E0mTFsHtUqaye8UCoLBq8sHCgo2IC7UaafhjGmVCG_SCdhZ9Z88uGj-uwMcg","keys":{"auth":"qX6AMD5JWbu41cFWE3Lk8w","p256dh":"BLxHw0IMtBMzOHnXgPxxMgSYXxwzJPxpgR8KmAbMMe1-eOudcIcUTVw0QvrC5gWOhZs-yzDa4yKooqSnM3rnx7Y"}};
    //your web certificates public-key
    const vapidPublicKey = 'BOcTIipY07N4Y63Y-9r7NMoJHofmCzn3Pu9g-LMsgIMGH4HVr42_LW9ia0lMr68TsTLKS3UcdkE3IcC52hJDYsY';
    //your web certificates private-key
    const vapidPrivateKey = 'web-certificate private key';

    var payload = JSON.stringify({
      "options": {
        "body": "PWA push notification testing fom backend",
        "badge": "/assets/icon/icon-152x152.png",
        "icon": "/assets/icon/icon-152x152.png",
        "vibrate": [100, 50, 100],
        "data": {
          "id": "458",
        },
        "actions": [{
          "action": "view",
          "title": "View"
        }, {
          "action": "close",
          "title": "Close"
        }]
      },
      "header": "Notification from toptips.fr-PWA Demo"
    });

    var options = {
      vapidDetails: {
        subject: 'mailto:[email protected]',
        publicKey: vapidPublicKey,
        privateKey: vapidPrivateKey
      },
      TTL: 60
    };

    webPush.sendNotification(
      pushSubscription,
      payload,
      options
    ).then(data => {
      return res.json({status : true, message : 'Notification sent'});
    }).catch(err => {
      return res.json({status : false, message : err });
    });

Envoi de notifications push depuis un backend PHP

Le package compositeur web-push-php est recommandé. Voici un exemple de code :

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

require __DIR__.'/../vendor/autoload.php';
use MinishlinkWebPushWebPush;
use MinishlinkWebPushSubscription;

// subscription stored in DB
$subsrciptionJson = '{"endpoint":"https://updates.push.services.mozilla.com/wpush/v2/gAAAAABh2…E0mTFsHtUqaye8UCoLBq8sHCgo2IC7UaafhjGmVCG_SCdhZ9Z88uGj-uwMcg","keys":{"auth":"qX6AMD5JWbu41cFWE3Lk8w","p256dh":"BLxHw0IMtBMzOHnXgPxxMgSYXxwzJPxpgR8KmAbMMe1-eOudcIcUTVw0QvrC5gWOhZs-yzDa4yKooqSnM3rnx7Y"}}';
$payloadData = array (
'options' =>  array (
                'body' => 'PWA push notification testing fom backend',
                'badge' => '/assets/icon/icon-152x152.png',
                'icon' => '/assets/icon/icon-152x152.png',
                'vibrate' => 
                array (
                  0 => 100,
                  1 => 50,
                  2 => 100,
                ),
                'data' => 
                array (
                  'id' => '458',
                ),
                'actions' => 
                array (
                  0 => 
                  array (
                    'action' => 'view',
                    'title' => 'View',
                  ),
                  1 => 
                  array (
                    'action' => 'close',
                    'title' => 'Close',
                  ),
                ),
),
'header' => 'Notification from toptips.fr-PWA Demo',
);

// auth
$auth = [
    'GCM' => 'your project private-key', // deprecated and optional, it's here only for compatibility reasons
    'VAPID' => [
        'subject' => 'mailto:[email protected]', // can be a mailto: or your website address
        'publicKey' => 'BOcTIipY07N4Y63Y-9r7NMoJHofmCzn3Pu9g-LMsgIMGH4HVr42_LW9ia0lMr68TsTLKS3UcdkE3IcC52hJDYsY', // (recommended) uncompressed public key P-256 encoded in Base64-URL
        'privateKey' => 'your web-certificate private-key', // (recommended) in fact the secret multiplier of the private key encoded in Base64-URL
    ],
];

$webPush = new WebPush($auth);

$subsrciptionData = json_decode($subsrciptionJson,true);


// webpush 6.0
$webPush->sendOneNotification(
  Subscription::create($subsrciptionData),
  json_encode($payloadData) // optional (defaults null)
);

Conclusion

Cet article a présenté les étapes nécessaires pour transformer une application web en PWA avec notifications push. Le code source est disponible ici et une démonstration est accessible ici. L'envoi de notifications push depuis le backend a été testé.

Auteur
France

Rédacteur tech, guides pratiques et astuces numériques.