Skip to content

💾 Persistance et scénarios offline

🎯 Objectif d'apprentissage

À la fin de ce chapitre, vous serez capables de :

  • Comprendre les différentes formes de persistance locale sur mobile
  • Distinguer bases de données locales, stockage clé-valeur, et stockage de fichiers.
  • Expliquer le fonctionnement des scénarios offline et du cache local.
  • Gérer les erreurs réseau, la synchronisation, et la résilience d'une application.

🤔 2.4.1 Introduction : pourquoi la persistance ?

Une application mobile n'est pas toujours connectée à Internet : tunnels, bâtiments, zones rurales, métro, mode avion... Pourtant, les utilisateurs s'attendent à ce qu'elle continue de fonctionner normalement, même hors ligne.

C'est là qu'intervient la persistance : la capacité d'une application à stocker des données localement sur l'appareil, afin qu'elles restent accessibles même sans connexion réseau.

La persistance permet par exemple de :

  • conserver des préférences utilisateur (langue, thème sombre),
  • mémoriser une session de connexion,
  • stocker des notes, listes, messages ou documents,
  • mettre en cache des données venant d'une API (ex. dernier rapport météo).

La gestion du mode offline repose sur trois briques complémentaires :

  1. le stockage léger (clé-valeur),
  2. la base de données locale (SQLite, Realm, etc.),
  3. la synchronisation entre le local et le serveur, lorsque la connexion revient.
💡 Exemple concret :

Une application de notes doit permettre d'ajouter, modifier ou supprimer des notes même hors ligne, puis synchroniser toutes les modifications avec le cloud dès que la connexion revient.

🎯 Objectif de cette section : comprendre les différentes solutions de stockage et comment assurer une expérience fiable en offline.

🔐 2.4.2 Stockage clé-valeur (préférences locales)

Le stockage clé-valeur est la forme de persistance la plus simple. Il permet d'enregistrer de petites informations sous forme de paires clé → valeur, un peu comme un dictionnaire.

🔧 À quoi ça sert ?

Ce type de stockage est parfait pour :

  • les préférences utilisateurs (thème sombre, langue, notifications),
  • les petits états internes de l'application (dernière page visitée, état d'un tutoriel),
  • les tokens d'authentification (JWT, OAuth).
  • les paramètres de configuration.

⚠️ Attention

Il ne convient PAS pour stocker des données volumineuses ou complexes (ex. liste de produits, historique complet, messages...)

🧑‍💻 Technologies selon plateforme

PlateformeOutilParticularités
AndroidSharedPreferencesSimple, rapide, non chiffré
iOSUserDefaultsPour les paramètres internes
Ionic / CapacitorPreferencesAPI JS → natif, unifiée iOS/Android
Fluttershared_preferencesStockage léger persistant

Exemple avec Capacitor Preferences :

typescript
import { Preferences } from '@capacitor/preferences';

await Preferences.set({ 
  key: 'theme', 
  value: 'dark'
});

👍 Bonnes pratiques :

  • Ne jamais stocker des informations sensibles en clair.
  • Ne pas dépasser quelques kilo-octets.

📂 2.4.3 Bases de données locales (SQLite, Room, CoreData)

Pour stocker des données plus volumineuses ou organisées, on utilise une base de données locale. Contrairement au stockage clé-valeur, elle permet d'effectuer :

  • des recherches (requêtes),
  • des tris,
  • des relations entre données,
  • des mises à jour complexes.

🤔 Pourquoi utiliser une base locale ?

  • Pour conserver une liste de données : notes, produits, messages, utilisateurs.
  • Pour permettre à l'app de fonctionner 100% offline.
  • Pour créer un cache local d'API (ex. charger les données une fois, puis les relire hors ligne).

🛠️ Solutions principales

PlateformeSolutionDescription
AndroidRoom (sur SQLite)ORM moderne, facile à utiliser en Kotlin
iOSCoreDataBase orientée objets, intégrée au système
Fluttersqflite, hiveSQLite ou base rapide clé-valeur
Ionic / Capacitor@capacitor-community/sqlitePlugin SQLite natif fiable
💡 Exemples concrets :
  • une app de recettes stocke les recettes consultées,
  • une app de notes gère des centaines d'entrées locales,
  • une app de films garde la liste des favoris hors ligne.

📌 Exemple visuel simple (schéma logique)

[ UI ] ⇄ [ ViewModel / Service ] ⇄ [ SQLite / CoreData ]

🎯 Une base locale permet un stockage organisé, performant et persistant, même lorsqu'on ferme l'application.

📇 2.4.4 Stockage de fichiers

Certaines applications doivent stocker des fichiers plutôt que des données structurées : photos, PDF, documents, images, scans, enregistrements audio...

📃 Types de fichiers concernés

  • Photos prises par l'utilisateur
  • Documents scannés,
  • Factures ou reçus PDF,
  • Captures vocales,
  • Fichiers téléchargés depuis Internet.

🧰 APIs selon la plateforme

PlateformeAPIUtilisation
AndroidFile, MediaStoreGestion des fichiers et médias
iOSFileManagerGestion de dossiers, lecture/écriture
Ionic / CapacitorFilesystemAPI JS → système de fichiers natif
Flutterfile_picker, path_providerChoix de fichiers, stockage local

💬 Exemple :

Une application de scan stocke les images des documents localement avant de les envoyer au cloud lorsque la connexion revient.

⚠️ Points d'attention

  • Gérer les permissions d'accès aux fichiers.
  • Contrôler la taille : photos en haute résolution = beaucoup d'espace.
  • Nettoyer les fichiers inutilisés pour éviter le stockage "fantôme".

🔌 2.4.5 Cache local et usage offline

Le cache local permet à une application d'afficher du contenu même lorsque la connexion Internet est lente ou indisponible. C'est un mécanisme clé pour offrir une expérience fluide et éviter les écrans "vides" lorsque l'app démarre sans réseau.

🤔 Pourquoi utiliser un cache ?

  • Pour réduire les appels réseau.
  • Pour accélérer l'affichage (les données sont déjà disponibles).
  • Pour permettre un usage offline partiel ou total.
  • Pour améliorer la perception de performance.

💬 Exemple :

Twitter, Instagram ou YouTube affichent le dernier contenu chargé, même hors ligne.

🧠 Types de cache

  • Cache mémoire (RAM)
    • Très rapide
    • Disparaît quand l'app est fermée
  • Cache disque
    • Plus lent, mais persiste entre les sessions
    • Parfait pour les données API ou images

📖 Stratégies de lectures des données

StratégieDescriptionCas d’usage
Cache-firstLire dans le cache, puis mettre à jour en fondApp météo, listes d’articles
Network-firstEssayer le réseau d’abord, puis fallback cacheChat, données sensibles
Stale-while-revalidateAfficher le cache immédiatement, puis rafraîchirRéseaux sociaux

🧑‍💻 Conseil développeur :

Toujours enregistrer un timestamp pour savoir si les données du cache sont encore "fraîches". Comme en restauration : on labellise tous les aliments dans le frigo !

walkin-cooler.jpg

🔄️ 2.4.6 Synchronisation online/offline

La synchronisation consiste à maintenir la cohérence entre les données locales et les données du serveur, même lorsque l'utilisateur travaille sans réseau.

🏋️ Les défis

  • Conflits entre données locales et distantes.
  • Envoi d'actions accumulées offline.
  • Fiabilité en cas d'arrêt brutal (batterie, crash, fermeture app).

📝 Stratégies de synchronisation

File d'attente locale (queue)

  • Enregistrer chaque action utilisateur dans une queue persistante (ex. : "Ajouter une note", "Modifier le profil", "Supprimer un élément").
  • À la reconnexion : rejouer la queue vers le serveur en respectant l'ordre.

Marqueurs de version (timestamps)

  • Chaque enregistrement possède une version (par ex. updatedAt).
  • En cas de modification simultanée, la plus récente (timestamp le plus récent) gagne.

Merge logique (résolution des conflits)

  • Stratégies courantes :
    • Dernière modification prioritaire (Last-Writer-Wins).
    • Priorité au serveur (authoritatif).
    • Priorité au local (offline-first).

💬 Exemple : Dans une app de notes, la version locale peut prévaloir si elle a été modifiée plus récemment.

❌ 2.4.7 Gestion des erreurs réseau

Le réseau mobile est instable par nature. Une bonne application doit anticiper les coupures, lenteurs et erreurs HTTP.

🔍 Détection du statut réseau

  • Android : ConnectivityManager
  • iOS : NNWPathMonitor
  • Ionic / Capacitor : Network plugin
  • Flutter : connectivity_plus package

Cela permet d'afficher des messages comme :

  • "Vous êtes hors ligne"
  • "Connexion lente, veuillez patienter"
  • "Connexion instable - affichage du cache"

🤕 Stratégie de résilience

  • Retry exponentiel : réessayer au bout de 1s → 2s → 4s...
  • Fallback vers le local (cache, BDD).
  • Désactivation automatique de certaines actions hors ligne.
  • Sauvegarde des actions dans une file locale (voir section précédente).

💬 Exemple : Une app de livraison peut empêcher l'envoi d'une commande hors ligne, mais continuer d'afficher les menus via le cache.

🧩 2.4.8 Activité pratique – Où stocker quoi ?

(clé–valeur / base de données / fichiers)

🎓 Objectif

Comprendre et comment stocker chaque type de données dans une application mobile.

🌇 Application CityQuest

Vous travaillez sur CityQuest, une application de chasse au trésor en ville.

🪙 Fonctionnalités

  • L'app affiche des quêtes géolocalisées (énigmes, points d'intérêt, QR codes à scanner).
  • Chaque quête rapporte des points et peut être validée avec une photo.
  • L'utilisateur peut voir :
    • ses quêtes en cours,
    • ses quêtes terminées.
  • L'app doit fonctionner raisonnablement même avec un réseau faible (cache local).

ℹ️ Remarque
On ne modélise ici ni la base de données serveur, ni tous les détails du profil utilisateur.
On se concentre uniquement sur les données stockées sur le téléphone.

🗒️ Consignes

Pour chaque donnée ci-dessous, indiquez dans quel type de stockage vous la placeriez :

  • Clé-valeur
  • Base de données locale
  • Fichiers
  • Pas besoin de persistance (si justifié)

✍️ Justifiez chaque choix en 1 phrase.

🔍 Données à analyser

  1. Token d'authentification (JWT, OAuth)
  2. Paramètre du thème (sombre/clair)
  3. Choix de l'utilisateur pour télécharger les images uniquement en Wi-Fi / en Wi-Fi + 5G
  4. Dernière position GPS connue de l'utilisateur (afin de centrer la carte au prochain lancement)
  5. Liste des quêtes disponibles dans la ville (titre, description, coordonnées GPS, difficulté, nombre de points)
  6. État d'une quête pour l'utilisateur (non commencée, en cours, terminée)
  7. Historique des quêtes terminées (dizaines ou centaines d'entrées)
  8. Cache des quêtes à proximité récupérées depuis l'API (pour qu'elles s'affichent même si le réseau est lent)
  9. Photo de validation d'une quête prise par l'utilisateur
  10. Fichiers de logs d'erreur pour envoi ultérieur au support (stack traces, messages techniques)

👉 Format de réponse suggéré

DonnéeType de stockage choisiJustification (1 phrase)
Token d'authentification
Paramètre du thème

🔗 2.4.9 Références