Application mobile hors ligne, ce que le mode déconnecté change dans un projet
Vos équipes travaillent dans des locaux techniques et des sous-sols où le réseau ne passe pas. Vous voulez qu'elles saisissent leur rapport sur place, pas le soir au dépôt. Voici ce que cette exigence ajoute à une application mobile, et les quatre choses qu'elle ne permettra toujours pas de faire.
Deux exigences très différentes derrière le même mot
« Il faut que ça marche hors ligne » recouvre deux demandes qui n'ont pas le même prix. La première est de consulter : l'utilisateur voit le planning et l'historique déjà chargés, même sans réseau. Elle se traite avec un cache et elle est presque gratuite sur les piles modernes. La seconde est de saisir : l'utilisateur crée, modifie, photographie, signe, et tout cela doit remonter plus tard, dans le bon ordre et sans doublon. C'est une autre affaire.
Nous avons livré la seconde. AquaPro Manager équipe des techniciens piscine en tournée : planning, rapports photo, signature du client directement sur l'écran. Son architecture est double, une application Flutter pour les techniciens et une application web Next.js pour les gérants, sur Cloud Firestore. L'interface de terrain est dessinée pour l'extérieur, grand contraste et gestes larges, et elle fonctionne hors connexion avec synchronisation automatique.
Comment une application stocke-t-elle les données saisies hors ligne ?
Dans une base de données locale, sur le téléphone, qui survit à la fermeture de l'application. L'application écrit d'abord sur l'appareil, rend la main à l'utilisateur, puis rejoue ces écritures vers le serveur quand le réseau revient. Sur un projet Flutter adossé à Firestore, ce mécanisme est fourni et actif par défaut.
La documentation de Firestore est explicite sur ce point, et sur son asymétrie : « For Android and Apple platforms, offline persistence is enabled by default », tandis que « For the web, offline persistence is disabled by default » (accès aux données hors ligne, documentation Firestore). Autrement dit, une application mobile native hérite du comportement hors ligne sans rien demander, et un projet web doit l'activer. Le seuil de cache par défaut est de 100 Mo, au-delà duquel Firestore tente périodiquement de nettoyer les documents anciens et inutilisés.
Un détail plus bas dans la même page change une interface. Hors réseau, la même requête ne répond pas de la même façon selon ce qu'elle demande : « When querying a collection, an empty result is returned if there are no cached documents. When fetching a specific document, an error is returned instead. » Une liste vide et une erreur ne se dessinent pas pareil. Un technicien qui ouvre son planning dans un sous-sol et lit « aucune intervention aujourd'hui » repart ; le même écran, s'il affichait « planning non chargé », le ferait remonter chercher du réseau. Nous distinguons donc les deux états dans la maquette, avant le développement.
Deuxième détail, celui-là coûteux à l'usage. Par défaut, le SDK parcourt tous les documents d'une collection dans le cache local pour exécuter une requête hors ligne, et la même page prévient que les performances s'en ressentent en déconnexion longue. L'indexation locale automatique qui corrige cela est désactivée par défaut et doit être réactivée à chaque démarrage. Une application de tournée qui ralentit au fil de la semaine n'a donc pas forcément un défaut de code : elle a un cache qui grossit sans index. Ce que ce socle couvre et où il s'arrête, nous l'avons détaillé dans notre article sur Firebase pour une application mobile.
Que se passe-t-il si deux personnes modifient la même donnée hors ligne ?
La dernière écriture qui se resynchronise écrase l'autre. Firestore le dit en une phrase : « For multiple changes to the same document, it's last write wins. » Il n'y a ni alerte, ni conflit remonté, ni version concurrente à arbitrer. Le technicien dont le rapport a disparu l'apprendra par son gérant, trois jours plus tard.
Cette règle décide de l'architecture des données, et elle se tranche donc avant la première ligne de code. Notre position est de réduire la concurrence plutôt que de l'arbitrer. Sur AquaPro Manager, les techniciens reçoivent leur planning la veille. Une information attendue la veille au soir voyage par une notification, canal qui a ses propres conditions de livraison : elles font l'objet de notre article sur les notifications push d'une application mobile.
Sur nos projets de terrain, quand une tournée est attribuée avant le départ, nous nous en servons pour réduire le nombre de rédacteurs d'un même rapport. Cela ne supprime pas toute concurrence : dès qu'un back-office modifie en journée un champ que le terrain est en train de saisir, les deux écritures se rejoignent, et nous séparons alors les champs que chaque rôle écrit.
Nous déconseillons l'autre voie, celle d'une interface de résolution de conflits générique. Personne, debout devant un local technique, ne comparera deux versions d'un rapport pour choisir la bonne. Quand la concurrence est inévitable, nous écrivons en ajout plutôt qu'en modification : chaque passage produit son propre document, horodaté et signé de son auteur, et la vue qui les additionne se calcule à la lecture. Deux écritures ne se croisent plus, parce qu'elles ne visent plus la même ligne.
Une PWA installée sur iPhone peut-elle travailler hors ligne ?
Oui, avec des réserves. Son stockage ne bénéficie pas des mêmes garanties que celui d'une application téléchargée sur une boutique, et la différence se joue sur la durée pendant laquelle les données survivent sans que personne n'ouvre l'application.
WebKit a aligné le stockage écrit par script sur la durée de vie des cookies posés côté client, en « deleting all of a website's script-writable storage after seven days of Safari use without user interaction on the site ». La liste des formes concernées inclut IndexedDB, LocalStorage et, c'est le point qui compte ici, les enregistrements et le cache de Service Worker (annonce WebKit du 24 mars 2020). La même annonce porte la nuance qui rend une PWA de terrain viable : « Web applications added to the home screen are not part of Safari and thus have their own counter of days of use. » Une application installée sur l'écran d'accueil et ouverte tous les jours ouvrés remet donc son compteur à zéro en permanence.
Cette nuance ne vaut qu'à deux conditions : que l'application ait été ajoutée à l'écran d'accueil, et qu'elle soit réellement ouverte chaque jour ouvré. Ce sont des consignes de déploiement et d'usage, pas des réglages de code, et c'est la première chose que nous vérifierions sur un outil interne ouvert chaque jour par les mêmes équipes. Nous ne ferions pas le même pari sur une application grand public ouverte une fois par mois.
Restent deux limites que nous signalons au cadrage. La synchronisation en arrière-plan, qui viderait la file d'attente après la fermeture de l'onglet, repose sur la Background Synchronization API ; MDN la classe en « Limited availability » et sa table de compatibilité ne donne ni Safari ni Firefox, Chrome la servant depuis sa version 49 (Background Synchronization API, MDN). Sur iPhone, la remontée se fait donc pendant que l'utilisateur a l'application sous les yeux, et un technicien qui retrouve du réseau en roulant, téléphone en poche, ne synchronise rien. Ensuite, le standard de stockage du WHATWG place par défaut un espace de stockage local en mode « best-effort », et il invite le navigateur sous pression disque à vider ces espaces avant de proposer à l'utilisateur d'effacer ceux qui sont passés en mode « persistent » (Storage Standard, WHATWG). Un téléphone professionnel plein de photos n'est pas un cas d'école.
Notre recommandation tient en une ligne. Si la saisie doit survivre à une semaine sans ouverture de l'application, ou si l'appareil est saturé en permanence, nous conseillons une application de boutique plutôt qu'une PWA. Le raisonnement complet est dans notre comparatif natif, hybride ou PWA.
Les règles métier que la déconnexion oblige à réécrire
La partie technique se traite. Ce qui surprend les clients, c'est le nombre de règles que personne n'avait formulées parce qu'elles allaient de soi tant que le serveur répondait. Quatre reviennent sur presque tous nos projets.
- la numérotation : un bon de travaux créé hors ligne ne peut pas recevoir un numéro de séquence unique, puisque seul le serveur sait quel est le dernier attribué ; nous numérotons à la remontée, donc le document imprimé sur place ne porte pas encore son numéro définitif ;
- l'heure : la seule horloge disponible hors ligne est celle du téléphone, et elle se règle à la main ; nous gardons l'heure de l'appareil comme donnée déclarée et celle du serveur comme heure d'enregistrement, parce qu'un litige ne les traite pas de la même façon ;
- les photos : une file d'attente de trois jours de rapports photo se mesure en mégaoctets et non en nombre de documents, et c'est ce volume qui décide du comportement au retour du réseau ;
- la suppression : une intervention annulée côté back-office pendant que le technicien la remplit hors ligne produit, à la resynchronisation, une donnée orpheline qu'il faut avoir décidé de garder ou de jeter.
Il y a un cinquième point, et c'est celui qui nous a coûté le plus cher à comprendre. L'authentification se déconnecte aussi. Sur AquaPro Manager, la connexion se fait par code à usage unique via Firebase Auth, ce qui est un bon choix pour des techniciens qui n'ont pas envie de retenir un mot de passe. Mais un utilisateur déconnecté de l'application alors qu'il n'a pas de réseau ne peut plus entrer : le code à usage unique arrive par le réseau. La durée de validité de la session, qui ressemble à un paramètre de sécurité anodin, devient une contrainte d'exploitation sur une application de tournée. La méthode de connexion se choisit donc aussi en fonction du réseau, et nous avons comparé à part ce que chaque méthode d'authentification d'une application mobile engage.
Ce que nous facturons en plus, et ce que nous ne promettons pas
Le hors ligne traverse chaque écran de saisie, et il se paie surtout dans la recette. Chaque formulaire se teste quatre fois : en ligne, en mode avion, en réseau dégradé qui répond au bout de trente secondes, et application fermée pendant la synchronisation. Nous avons sous-estimé ce poste sur nos premiers projets de terrain, en le chiffrant comme du développement alors que c'est du test.
C'est pour cela que ces scénarios s'automatisent : un comportement qui dépend de l'état du réseau ne se vérifie pas à la main à chaque livraison.
Il y a enfin ce que nous refusons d'annoncer comme fonctionnant hors ligne, parce que ces opérations ont besoin de la vérité du serveur. Un paiement. Une allocation de stock quand plusieurs techniciens tirent sur le même dépôt. Une première connexion. Un numéro de pièce comptable définitif. Un prestataire qui vous dit que tout fonctionnera hors ligne n'a pas regardé ces quatre cas, ou ne compte pas vous en parler avant la recette.
Questions fréquentes
Une application mobile peut-elle fonctionner sans aucune connexion internet ?
Que devient une donnée que deux personnes ont modifiée hors ligne ?
Le mode hors ligne tient-il sur une PWA installée sur iPhone ?
Combien coûte le mode hors ligne sur un projet mobile ?
À votre place, nous n'écririons pas « l'application doit fonctionner hors ligne » dans le cahier des charges. Nous écririons la liste des écrans qui doivent fonctionner sans réseau, et surtout celle des écrans qui n'en ont pas besoin. Cette seconde liste est presque toujours la plus longue, et c'est elle qui fait baisser le devis.
Si vos équipes notent encore sur papier avant de ressaisir le soir, les deux listes existent déjà sans que vous les ayez écrites. Nous commençons nos missions de développement d'application mobile par les mettre sur une page.