Passer au contenu principal

La stratégie de cache en Flutter

  • Si votre utilisateur a une mauvaise connexion ou n’est pas connecté à internet.
  • Pour limiter les appels réseaux, notamment pour de la donnée qui ne nécessite pas d’être mise à jour régulièrement.
  • Pour stocker de la donnée sensible (on reparlera de ce sujet plus tard).

  • Le cache réseau correspond plutôt aux data dites “métier” de l’application et est donc plus éphémère par nature.
  • À l’inverse, le cache utilisateur, qui correspond généralement aux Access Token, Resfresh Token, password etc… doit être plus sécurisé, encrypté et non accessible par l’utilisateur (utilisation de Keychain pour iOS et EncryptedSharedPreferences pour Android, par exemple).
  • Concrètement, un refresh token a une plus longue validité qu’un affichage d’un message dans une messagerie, ce qui causerait un appel réseau sur le refresh token inutile.

Maintenant que nous avons une vision primaire de ce qu’est le cache, rentrons dans le vif du sujet !

Au début du projet, l’arborescence de dossiers ressemble à ça :

  • On crée une instance Hive à l’instanciation de CacheStorage.
  • À chaque fois que nous manipulons de la donnée, on ouvre la boxe grâce à hiveBoxName et la méthode appelée est exécutée (get, write, delete…).
  • On accède à la donnée voulue grâce à sa key.

  • defaultTTLValue correspond au temps de validité de notre cache. Une fois cette période de validité dépassée, la donnée stockée est considérée comme invalide.
  • _storeCacheData() permet de stocker la donnée grâce au CacheWrapper.
  • _isValid() vérifie si le cache récupéré est toujours valide grâce au defaultTTLValue.
  • invokeAsync() va déclencher un appel réseau pour récupérer la donnée en utilisant la méthode fournie par asyncBloc. Il appellera ensuite _storeCacheData() pour stocker cette dernière, puis la renvoyer.
  • fetchCacheData() permet de récupérer la donnée stockée en cache via sa key. Une fois le json reçu et décodé par leCacheWrapper, si la donnée est toujours valide, on retourne un objet Dart typé et utilisable grâce au serializerBloc.
  • applyStrategy() est la méthode qui sera implémentée par nos différentes stratégies pour qu’elles puissent s’exécuter.

 

AsyncOrCache

Cette stratégie va déclencher un appel réseau dans le but de récupérer de la donnée. Si de la donnée est récupérée, elle sera stockée et renvoyée repository. Dans le cas o une erreur est retournée suite à notre appel réseau (erreur 401, 403, 404, 500…), la stratégie va récupérer de la data stockée précédemment en cache puis la retourner. S’il n’y a pas de donnée en cache ou qu’elle est invalide, on retourne l’erreur précédemment levée ainsi qu’une valeur null qui sera gérée dans le gestionnaire d’état.

CacheOrAsync

Cette stratégie est similaire à celle du dessus, mais dans le sens inverse. On va d’abord checker si de la donnée est stockée en cache et, si le retour de fetchCacheData() est null, on déclenchera un appel réseau. La suite est identique au comportement de la stratégie AsyncOrCache.

JustAsync

Cette stratégie déclenche seulement un appel réseau, avec le même comportement qu’AsyncOrCache.

JustCache

Cette dernière utilise donc uniquement la donnée stockée en cache.

  • defaultSessionName permet de donner un nom à notre session cache et de ce fait la manipuler facilement. Par exemple, on peut envisager que dans une application, chaque utilisateur ait une session de cache associée à son compte sur le même appareil. On pourra donc aisément retrouver les données stockées en utilisant firstName + lastName + id de l’utilisateur comme defaultSessionName.
  • from() créer une instance de StrategyBuilder qui prend un type générique (ce qui permet donc de typer la data comme on le souhaite). Une key est passée en paramètre afin de créer le nom de notre future hiveBox. Une instance de CacheStorage est également passée afin que le StrategyBuilder puisse s’en servir. withSession() permet donc de nommer la session de cache actuelle.
  • clear() permet de vider la session de cache, soit entièrement, soit précisément via le prefix qui correspond à la key de la donnée stockée.
  • withAsync() permet de récupérer l’appel réseau souhaité par l’utilisateur, qui sera utilisé dans la méthode invokeAsync().
  • withSerializer() correspond au sérialiseur/désérialiseur de notre donnée, qui sera responsable de la transformation du JSON reçu par le cache en objet typé utilisable dans le code et vice-versa.
  • withTtl() permet de définir le defaultTTLValue pour la durée de vie du cache.
  • withStrategy() permet de choisir la stratégie souhaitée. L’utilisation d’un Singleton laisse à l’utilisateur la possibilité de créer d’autres types de stratégie à sa guise (plus customisable qu’une enum par exemple).
  • execute() est la dernière méthode appelée qui permet de déclencher applyStrategy() et par la suite de retourner la donnée récupérée.

  • from() permet de préciser quel est le type du dto attendu ainsi que la key de la session de cache.
  • withSerializer() va injecter la méthode de désérialisation pour la donnée.
  • withAsync() injecte l’appel réseau nécessaire pour récupérer la donnée.
  • withStrategy() permet de choisir quelle stratégie l’on souhaite mettre en place.
  • execute() permet de déclencher la stratégie de cache et attend en retour une donnée renvoyée par cette dernière.

Romain / Développeur mobile chez Beapp

Retrouvez Romain sur Medium ! 📝

Romain Gréaume

Auteur Romain Gréaume

Plus d'articles par Romain Gréaume

Laisser un commentaire