Automatiser tes captures d'écran App Store

App Store Connect demande des captures d’écran par langue et par taille d’appareil. Pour Foodproof, ça fait 9 écrans à capturer, en français puis en anglais. Les refaire à la main à chaque changement d’UI, c’est le genre de tâche répétitive qui donne juste envie de ne pas faire de release. Voici comment on l’a automatisée.

L’idée : laisser l’app se piloter elle-même

Plutôt que d’écrire un script qui simule des taps sur l’écran (fragile, ça casse au moindre changement de layout), on a mis la logique de navigation directement dans l’app. Quand l’app démarre en mode capture, AppNavigator.onReady déclenche une fonction runScreenshotTour() qui bascule la langue voulue puis navigue elle-même à travers les 9 écrans, en prenant une capture à chaque étape avec captureScreen() de react-native-view-shot.

L’ordre des écrans est fixe :

  1. 01_scanner
  2. 02_result_compatible
  3. 03_result_not_compatible
  4. 04_result_uncertain
  5. 05_product_detail
  6. 06_home
  7. 07_history
  8. 08_profile
  9. 09_profile_edit

Le script

C'est quoi le simulateur iOS ? Un iPhone virtuel qui tourne sur ton Mac, fourni avec Xcode. `xcrun simctl` est l'outil en ligne de commande pour le piloter : lister les simulateurs disponibles, lancer une app dessus, ou fouiller dans ses fichiers.

Le script take-screenshots.sh à la racine du repo s’utilise comme ça :

./take-screenshots.sh fr
./take-screenshots.sh en

Il ne fait pas grand-chose lui-même, c’est presque le point : il relance l’app sur le simulateur actuellement démarré (xcrun simctl terminate puis launch), attend que le tour de captures se termine (35 secondes, le temps que l’app traverse ses 9 écrans), puis va chercher les PNG écrits par react-native-view-shot dans le dossier temporaire du conteneur du simulateur, et les copie renommés dans website/screenshots/<langue>/.

# extrait de la logique du script
xcrun simctl get_app_container <device_id> com.yabaa.foodproof data
# puis recherche des PNG plus récents qu'un marqueur créé avant le lancement

Pas de fragilité liée aux coordonnées d’écran, pas de dépendance à un outil de test end-to-end. Juste l’app qui sait se mettre elle-même dans le bon état.

Le piège du flag qui se réinitialise

Le mode capture d’écran repose sur un flag, _isScreenshotMode, dans screenshotMode.ts. Le vrai piège : ce flag doit être mis à true à deux endroits, la déclaration au niveau module et à l’intérieur de la fonction initScreenshotMode(). Si tu ne le mets qu’au premier endroit, l’appel à initScreenshotMode() depuis index.js l’écrase avec initialProps?.screenshotMode === true, qui vaut false par défaut. Résultat : le mode se désactive tout seul juste après le démarrage, et tu te demandes pendant un moment pourquoi rien ne se passe.

Ce qu’il faut avoir en place avant de lancer le script

  • Un simulateur démarré (le script cherche celui qui est actuellement “booted” via xcrun simctl list devices booted)
  • Metro qui tourne (npx react-native start)
  • L’app installée avec le bon encodage de terminal :
LANG=en_US.UTF-8 npx react-native run-ios --simulator "iPhone 16 Pro Max"

Petite note honnête : le commentaire en haut du script mentionne encore “iPhone 14 Plus” alors que la doc interne recommande maintenant l’iPhone 16 Pro Max. Un reliquat qu’on n’a pas nettoyé, comme souvent avec les scripts qu’on ne relit qu’en diagonale une fois qu’ils marchent.

Le résultat

Les images atterrissent directement dans website/screenshots/{fr,en}/, qui est aussi le dossier servi par le site web sans étape de copie intermédiaire. Une seule commande, deux langues, neuf écrans, zéro clic manuel.