Vérifier le cache HTTP des images iOS sur un Mac distant

Vérifier le cache HTTP des images iOS sur un Mac distant

Une vignette s’affiche normalement au premier chargement, puis presque instantanément au second. Il est tentant d’en conclure que « le cache fonctionne ». Pourtant, la vue conserve peut-être simplement l’objet image en mémoire. Pour vérifier le cache HTTP des images iOS, il faut observer ce que reçoit le serveur : la première requête doit obtenir les octets de l’image ; une fois la réponse périmée, le client doit la revalider à l’aide de l’ETag ; la lecture hors ligne doit faire l’objet d’un test distinct. Un rafraîchissement rapide ne suffit pas.

Définir le périmètre du test

Exécutez le serveur de test et le simulateur iOS sur le même Mac distant. Depuis le simulateur, utilisez localhost pour éviter que les variations d’une liaison entre régions ne faussent l’analyse du cache. Choisissez un petit PNG, placez-le dans le répertoire de travail courant et nommez-le thumb.png. Ne changez ni l’URL, ni le compte, ni le décodeur d’image pendant cette série de tests.

Trois résultats observables sont à vérifier :

Scénario Ce que le serveur doit voir Ce que l’application doit obtenir
Première requête Pas de If-None-Match, réponse 200 Les octets de l’image
Requête suivante Un ETag correspondant, réponse 304 L’image mise en cache, après revalidation
Lecture hors ligne explicite Aucune nouvelle requête L’image stockée, ou un échec de cache clairement identifié

Une réponse 304 ne contient pas les octets de l’image. Si l’application peut tout de même l’afficher, c’est parce que le client utilise le contenu stocké avec le résultat de la revalidation. Ne confondez pas « l’interface a reçu une image » avec « le serveur a renvoyé l’image ».

Démarrer un serveur qui journalise les requêtes conditionnelles

Dans le répertoire contenant l’image, enregistrez le script suivant sous fixture.py. Il n’écoute que sur l’adresse de bouclage locale : le port de test n’est pas exposé à l’extérieur. max-age=0 impose une revalidation lors des requêtes normales suivantes ; l’ETag fixe rend le déroulement facile à observer.

from http.server import BaseHTTPRequestHandler, HTTPServer
from pathlib import Path

BODY = Path("thumb.png").read_bytes()
TAG = '"thumb-v1"'

class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path != "/thumb":
            self.send_error(404)
            return
        matched = self.headers.get("If-None-Match") == TAG
        status = 304 if matched else 200
        print(f"If-None-Match={self.headers.get('If-None-Match')!r} status={status}", flush=True)
        self.send_response(status)
        self.send_header("ETag", TAG)
        self.send_header("Cache-Control", "public, max-age=0")
        if not matched:
            self.send_header("Content-Type", "image/png")
            self.send_header("Content-Length", str(len(BODY)))
        self.end_headers()
        if not matched:
            self.wfile.write(BODY)

HTTPServer(("127.0.0.1", 8765), Handler).serve_forever()

Après avoir lancé python3 fixture.py, vérifiez d’abord dans un autre terminal que curl -si http://127.0.0.1:8765/thumb -o /dev/null obtient une réponse 200, puis exécutez :

curl -si -H 'If-None-Match: "thumb-v1"' \
  http://127.0.0.1:8765/thumb -o /dev/null

Le journal doit afficher successivement 200 et 304. Cette étape confirme uniquement que le serveur de test se comporte comme prévu ; elle ne prouve pas qu’iOS envoie automatiquement une requête conditionnelle.

Vérifier les requêtes du simulateur avec une même session

Pour la cible de test, partez d’une configuration URLSessionConfiguration.default distincte, associez-lui un URLCache doté d’une capacité mémoire suffisante et conservez la politique .useProtocolCachePolicy. Effectuez deux requêtes successives vers la même URL avec la même session, attendez chaque fois la fin de la tâche et vérifiez que le PNG obtenu peut être décodé. Consultez le journal du serveur : la première requête doit produire une réponse 200 ; si la seconde parvient effectivement au serveur, elle doit contenir If-None-Match et recevoir une réponse 304.

let configuration = URLSessionConfiguration.default
configuration.urlCache = URLCache(
    memoryCapacity: 8 * 1024 * 1024,
    diskCapacity: 0
)
configuration.requestCachePolicy = .useProtocolCachePolicy
let session = URLSession(configuration: configuration)
let url = URL(string: "http://localhost:8765/thumb")!

Faites utiliser cette même session aux deux chargements d’image de l’application, sans recréer la configuration à chaque fois. Si la politique de sécurité des transports bloque le HTTP local dans la cible de test du simulateur, vous pouvez autoriser le réseau local uniquement dans la configuration de cette cible ; ne reportez pas cette exception de développement dans l’application publiée. Si le journal du serveur ne contient aucune seconde requête, vérifiez d’abord si l’image a été réutilisée depuis la mémoire et si un second chargement a réellement été lancé, avant d’examiner la politique de cache. Le seul temps d’exécution ne permet pas de distinguer ces cas.

Vérifier l’ETag après modification de l’image

Remplacez la valeur de TAG côté serveur par '"thumb-v2"' et remplacez également thumb.png. Redémarrez le serveur, puis relancez une requête. L’ancien ETag ne doit plus correspondre : la nouvelle réponse doit être 200 et l’application doit recevoir la nouvelle image. Dans un projet réel, l’ETag doit évoluer avec la version du contenu. Réutiliser l’ancien identifiant après avoir remplacé le fichier conduirait la revalidation à conserver l’ancienne image à tort.

Tester la lecture hors ligne séparément

Effectuez d’abord une requête 200 en ligne et vérifiez que la réponse peut être stockée. Arrêtez ensuite le serveur de test, créez pour la même URL une requête utilisant la politique .returnCacheDataDontLoad, puis lancez-la avec la session qui détient toujours le cache initial. Une réussite signifie que les données ont été lues dans le cache. Un échec doit être consigné comme un échec de cache, sans être automatiquement attribué à une anomalie du serveur. À la fin du test, vérifiez le journal du serveur : la requête hors ligne ne doit y avoir ajouté aucune entrée, que ce soit avant ou après l’arrêt du service.

Cette politique hors ligne résulte d’un choix explicite de l’application ; elle ne signifie pas qu’une requête par défaut réutilisera nécessairement l’ancienne image en l’absence de réseau. La capacité du cache, les nettoyages effectués par le système et les en-têtes de réponse influencent tous les chances de trouver les données en cache. L’interface doit prévoir un état de remplacement en cas d’échec et permettre une nouvelle tentative au retour du réseau, plutôt que de supposer qu’une image déjà ouverte reste forcément disponible.

Écarter les fausses conclusions et consigner les résultats

Pour chaque série, notez l’URL de test, l’ETag, les en-têtes de cache de la réponse, la suite des codes d’état dans le journal du serveur et la version de l’image finalement affichée par l’application. Si vous obtenez 200→200, vérifiez d’abord que la réponse peut être mise en cache et que les deux requêtes utilisent la même URL et le même cache. Si le serveur indique 304 mais que l’interface affiche toujours l’ancienne image, vérifiez qu’un ancien objet conservé par la vue ne remplace pas l’image après le traitement de la réponse. Si une image modifiée produit encore une réponse 304, assurez-vous d’abord que son identifiant côté serveur a bien changé.

Enfin, répétez le premier test avec une application fraîchement installée dans le simulateur pour éviter qu’un cache issu d’un essai précédent ne fausse la conclusion. Ces relevés remplacent « l’image semble se charger vite » par quatre résultats vérifiables — 200, 304, accès hors ligne réussi et échec de cache — et facilitent les nouveaux tests lorsque l’équipe modifie le service d’images ou la politique de cache du client.

Questions fréquentes

Comment confirmer que le serveur a réellement répondu 304 ?

Consultez son journal pour voir If-None-Match et le statut envoyé. L’image visible dans l’application ne suffit pas.

Pourquoi une image déjà chargée manque-t-elle hors ligne ?

L’entrée peut avoir été évincée ou interdite de stockage. Prévoyez une lecture explicite du cache et un état pour les données absentes.

Machine physique dédiée

Préparez un Mac pour votre prochain projet de développement à distance

Choisissez un modèle et un emplacement selon vos besoins, puis connectez-vous via le partage d’écran, VNC ou SSH. Les prix et disponibilités réels sont ceux indiqués en temps réel dans la console.

Configurer un Mac distant