Une application HTML5 "desktop" en mode offline

html5

Durant une de mes récentes missions j’ai eu la chance de développer une application HTML5 en mode offline. Cette application a pour coeur de métier le calcul de structures dans le bâtiment. En gros, l’ingénieur bâtiment va sur un chantier avec son laptop ou sa tablette, ouvre l’application et rentre des données de mesures physiques pour voir si à tout hasard le bâtiment ne risque pas de s’écrouler ^^. Toute la logique métier est donc côté client et une connexion au serveur web permet juste de télécharger l’application une première fois, puis de faire des mises à jour.

La grande question est pourquoi avoir choisi des technos Web pour faire une application “desktop” offline ? Tout d’abord c’était un pari sur l’avenir et ensuite plusieurs devices clients étaient ciblés, notamment des laptops et des tablettes, et ça HTML5 sait bien faire. L’argument récurrent contre HTML5 est la compatibilité navigateur. Pour une application interne à l’entreprise, on aurait pu cibler un seul navigateur mais ça n’a pas été notre cas. Le client voulait de la compatibilité Firefox, Chrome et même IE8 ! Heureusement ils ont bien voulu de Google Chrome Frame.

Dans cet article, je vous présenterai donc les différentes technos, APIs que j’ai utilisé pour développer cette application mais surtout je vous dévoilerai les 20% d’écueils qui ont pris 80% de mon temps !

Mode Offline

Mon ingénieur en bâtiment, il n’aura pas forcément de connexion internet sur les chantiers. Il lui faut donc une application capable de fonctionner en mode offline. HTML5 a introduit l’interface ApplicationCache qui va permettre de mettre en cache l’application web et ainsi de se passer d’une connexion internet les fois suivantes.

Le fichier cache manifest

Le fichier cache manifest est un simple fichier texte qui va référencer tous les fichiers de votre application qui doivent (ou ne doivent pas) être mis en cache. Voici un exemple de fichier manifest qui demande à mettre en cache les fichiers index.html et css/style.css :

CACHE MANIFEST<br></br>
index.html<br></br>
css/style.css<br></br>

Ce fichier (“manifest.appcache” par exemple mais vous pouvez lui donner n’importe quel nom ou extension) doit être référencé par votre page dans la balise :

<html manifest="manifest.appcache"><br></br>
...<br></br>
<br></br>

Voilà rien de plus simple. Mais on peut faire un peu plus compliqué. Le fichier cache manifest peut se décomposer en 3 sections :

  • La section CACHE : c’est la section par défaut, celle qui va référencer les fichiers à mettre en cache
  • La section NETWORK : c’est l’opposé de la section CACHE, ici on va référencer les fichiers qui nécessiteront toujours un accès réseau
  • La section FALLBACK : dans cette section on peut proposer des pages de “secours” si des ressources ne sont pas accessibles.

En exemple :

CACHE MANIFEST<br></br>
# on peut omettre cette ligne car CACHE: est la section par défaut<br></br>
CACHE:<br></br>
index.html<br></br>
css/style.css<br></br>
# Ressources qui nécessitent que l'utilisateur soit online<br></br>
NETWORK:<br></br>
img/logo.png<br></br>
# offline.html sera utilisé si online.html n'est pas accessible<br></br>
# offline.html sera utilisé à la place des autres fichiers html si ils ne sont pas accessibles<br></br>
FALLBACK:<br></br>
online.html offline.html<br></br>
*.html offline.html<br></br>

Ecueils

Pas de Wildcards dans la section CACHE

On peut utiliser des wildcards dans les sections NETWORK et FALLBACK mais pas dans la section CACHE ! Si vous avez beaucoup de fichiers dans votre application ça peut être ennuyeux. Je vous conseille d’utiliser un petit script pour générer une première fois votre fichier, sous Unix par exemple :

echo "CACHE MANIFEST" > manifest.appcache; find . -type f | sed "s#^\./##" >> manifest.appcache<br></br>

Type MIME du fichier cache manifest

Une erreur fréquente est d’oublier de vérifier que votre serveur web sert le fichier cache manifest avec le bon type MIME. Un fichier cache manifest doit avoir le type MIME “text/cache-manifest”. Par exemple dans Apache, vous pouvez rajouter cette ligne dans le fichier de configuration :

AddType text/cache-manifest .appcache<br></br>

Mise à jour du cache

Tiens bizarre, vous avez modifié votre fichier index.html mais lorsque vous rechargez la page, vous ne voyez pas vos modifications !? C’est normal si vous avez mis en cache votre fichier index.html ! C’est bien là le principe du cache 🙂 Du coup si vous voulez que votre navigateur mette à jour son cache avec votre nouveau fichier, vous devez modifier le fichier cache manifest.
Je vous conseille d’utiliser une ligne de commentaire avec un numéro de version que vous incrémenterez lorsque vous déploierez une nouvelle version de votre application.

CACHE MANIFEST<br></br>
# Version 2<br></br>
index.html<br></br>
...<br></br>

(note: le cache peut également être mis à jour programmatiquement via l’api ou lorsque vous effacez le cache du navigateur)

C’est trop bien le cache HTML5 mais en mode développement c’est pas pratique

Effectivement, c’est pas pratique car pour voir vos modifications vous devez en permanence modifier votre fichier cache manifest ! Là plusieurs solutions s’offrent à vous :

  • configurez votre serveur web pour que vos fichiers expirent tout de suite
  • ne pas utiliser de cache en développement

Ne pas mettre en cache le fichier cache manifest lui-même

Car si vous le faites, votre fichier cache manifest sera en cache et votre navigateur ne verra pas les modifications apportées au fichier cache manifest et ne mettra donc pas à jour son cache ! Il faudra attendre le temps d’expiration du fichier.

Double Rechargement

Lors du téléchargement d’une nouvelle version de votre application, il faut reloader 2 fois la page : une fois pour mettre à jour son cache, une 2ème fois pour vraiment charger la nouvelle version en cache. Pour éviter ce double rechargement à votre utilisateur, vous pouvez utiliser un peu de code Javascript qui va faire ce boulot pour vous :

// On vérifie si un nouveau cache est disponible au chargement de la page window.addEventListener('load', function(e) { window.applicationCache.addEventListener('updateready', function(e) { if (window.applicationCache.status == window.applicationCache.UPDATEREADY) { // Là le navigateur a téléchargé un nouveau cache // On change le cache et on recharge la page window.applicationCache.swapCache(); window.location.reload(); } else { // Le cache Manifest n'a pas changé, on ne fait rien } }, false); }, false);

Api Localstorage

Robert, mon ingénieur en bâtiment, il avait l’habitude avec son logiciel desktop de sauvegarder son travail, de revenir sur son chantier le lendemain et de reprendre là où il s’était arrêté. Si on était sur une application Web classique, on sauvegarderait notre travail dans une base de données. Mais en offline, il faut composer avec ce que le navigateur nous propose. Il y a les cookies, mais bon la taille maximale d’un cookie est de 4ko, ça fait pas beaucoup. Ce qu’il nous faut c’est donc un espace de stockage :

  • de grande capacité
  • sur le client
  • qui persiste au-delà d’un rechargement de page
  • qui n’est pas transmis au serveur

Dans un premier temps on va utiliser le Web Storage et plus particulièrement le LocalStorage qui se comporte simplement comme une Map clé-valeur. Celui-ci répond à tous nos besoins. Par exemple, sa limite de stockage est de 5Mo par nom de domaine. Cette limite est évoquée dans la spécification mais ce nombre peut différer selon les implémentations.

Ecueils

String-String

Le principal problème est que le LocalStorage ne propose qu’un mapping clé-valeur où clé et valeur ne peuvent être que des chaines de caractères ! Si vous sauvegardez un int ou un objet, celui-ci sera transformé en chaine de caractères. Lorsque vous voudrez le charger, il faudra coder une petite étape intermédiaire pour retrouver votre objet initial (parseInt() ou JSON.parse() par exemple). Mais si vous ne voulez pas vous embêter, vous pouvez utiliser le petit framework Locache qui possède une API simple de cache et qui permet notamment de sauvegarder vos objets dans le LocalStorage et de le récupérer en tant que tel.

Api File, Filesystem

Robert, qui dépose tous les matins ses enfants à l’école, il est content de pouvoir sauvegarder son travail dans son navigateur. Mais avant il le faisait dans des fichiers qu’il pouvait voir en vrai sur son disque dur. En plus, ça lui arrivait d’envoyer son fichier à son collègue Miguel pour qu’il y jette un oeil. Ok… pas de problème…
Dans une application web classique, c’est le serveur qui envoie un binaire à votre navigateur et ce dernier vous propose de le sauvegarder quelque part sur le filesystem. Mais là je vous rappelle, on travaille en mode offline.

HTML5 introduit plusieurs APIs permettant de gérer des fichiers :

  • l’API File permet de lire des fichiers provenant de votre système de fichiers au niveau OS
  • les APIs FileWriter et FileSystem permettent de gérer un système de fichier à l’intérieur de votre navigateur. Ce système de fichier est cantonné à un nom de domaine et évidemment vous ne pouvez pas y accéder en dehors de votre navigateur courant. Mais le W3C a tout prévu et l’interface FileWriter possède une méthode saveAs(data, filename) permettant de sauvegarder le fichier sur votre filesystem au niveau OS. Malheureusement cette méthode n’est supporté que dans Chrome. Heureusement, il existe un polyfill pour les autres browsers : FileSaver.js.

Ecueils

Support des navigateurs

Ici le principal écueil est le support disparate des navigateurs des APIs de fichiers. D’une part, les APIs fichiers de HTML5 sont compliqués car scindés en différentes API et de l’autre côté, certains navigateurs supportent une Api mais pas l’autre. C’est facile de s’y perdre. Je vous conseille d’utiliser le site caniuse pour se tenir au courant des différentes compatibilités navigateurs.

Modélisation 2D

Autre truc sympa que l’application doit faire, c’est modéliser les différentes structures (poteaux, dalles, …) des bâtiments. Oui, Robert comprend mieux avec des schémas et des dessins. Pas vous ?

Canvs_vs_SVG

🙂 Bon ici on était confronté à deux choix : Canvas ou SVG. On a essayé les deux :

et à vrai dire on était plutôt satisfait des deux. En ce qui nous concerne, ce qui a fait la différence, c’est que SVG c’est du vectoriel. D’une, l’application précédente utilisait du vectoriel et de deux, ces modélisations devaient être imprimé pour être incorporé dans un rapport.

Framework Javascript, CSS

Bien sûr au-delà de HTML5, nous avons utilisé plénitude d’outils et framework Javascripts, CSS. Je ne vais pas les détailler ici, surtout que mes collègues s’y sont déjà attelés :

Conclusion

J’ai oublié de préciser que ce développement n’était pas un développement de zéro mais une migration d’application desktop écrite en C++ vers une application web offline écrite en HTML Javascript ! On commence à voir des entreprises de moins en moins frileuses par rapport à ces nouvelles technologies Web et tant mieux ! Le Web existe maintenant depuis plus de 20 ans et est utilisé partout, par tout le monde et tout le temps.

Je pense que HTML5 a de beaux jours devant lui et notamment dans le développement d’applications d’entreprise dites desktop. Les APIs sont là, les implémentations ne suivent pas toujours mais la communauté HTML/Javascript/CSS est tellement bouillante que de nombreux frameworks émergent pour combler les trous. La convergence applications web, applications desktop et applications mobiles sera sans doute HTML5 (ou HTML6 ^^). Seul l’avenir nous le dira.

HTML5_sticker

Pour aller plus loin, découvrez notre formation HTML 5 : http://www.ippon.fr/formation/html5-css3