Article d’ingénierie

Précontrôler Storyboard et XIB avec ibtool sur Mac cloud

Précontrôler Storyboard et XIB avec ibtool sur Mac cloud

Un Storyboard peut s’ouvrir sans problème sur la machine de développement, puis faire échouer une tâche d’archivage distante en plein milieu après une fusion. Retélécharger les dépendances, lancer le simulateur ou nettoyer DerivedData ne sert alors à rien : le véritable problème peut n’être qu’un outlet devenu invalide, un module incorrect pour une classe personnalisée, ou un attribut XIB non pris en charge par la cible de déploiement actuelle. Pour l’intégration continue sur un Mac cloud, il est plus efficace de commencer par compiler séparément les ressources d’interface avec ibtool, puis de décider s’il faut lancer la compilation complète.

Avancer le contrôle des ressources d’interface

ibtool est fourni avec Xcode et doit être invoqué via xcrun, plutôt qu’au moyen d’un chemin d’outil codé en dur dans le script. Il peut lire les Storyboards et les XIB, produire des errors, warnings et notices, puis compiler les fichiers sources en storyboardc ou en nib. Cette étape ne nécessite pas de lancer le simulateur et peut être placée après la résolution des dépendances, mais avant xcodebuild archive.

Commencez par vérifier le répertoire de développement réellement utilisé par le nœud d’exécution :

set -euo pipefail

xcode-select -p
xcrun --find ibtool
xcodebuild -version
xcrun ibtool --version

Ces informations doivent être consignées dans le journal de la tâche. Si l’équipe conserve plusieurs versions de Xcode sur un même Mac cloud, le pipeline doit définir explicitement DEVELOPER_DIR, puis supprimer cette variable d’environnement à la fin de la tâche afin d’éviter qu’un job ultérieur n’hérite de la mauvaise chaîne d’outils.

Le fait qu’un fichier d’interface s’ouvre dans l’éditeur graphique ne garantit pas qu’il puisse être compilé avec la version de Xcode, la cible de déploiement et le contexte de module spécifiés. Le contrôle CI doit reproduire la chaîne d’outils de publication, et non l’environnement de bureau d’un développeur donné.

Analyser et compiler séparément les Storyboards et les XIB

Le script ci-dessous analyse les fichiers d’interface du dépôt et crée un répertoire de sortie distinct pour chaque entrée. Il est important d’exclure les répertoires de compilation et de dépendances afin de ne pas contrôler plusieurs fois les fichiers générés ou les ressources tierces.

#!/usr/bin/env bash
set -euo pipefail

root="${1:-$PWD}"
target="${IPHONEOS_DEPLOYMENT_TARGET:-16.0}"
work="${TMPDIR:-/tmp}/ibtool-preflight"
log="$work/ibtool.log"

rm -rf "$work"
mkdir -p "$work/out"
: > "$log"

find "$root" \
  \( -path "*/DerivedData/*" -o -path "*/build/*" -o -path "*/Pods/*" \) -prune \
  -o \( -name "*.storyboard" -o -name "*.xib" \) -print0 |
while IFS= read -r -d '' source; do
  relative="${source#"$root"/}"
  safe_name="$(printf '%s' "$relative" | tr '/ ' '__')"

  case "$source" in
    *.storyboard)
      output="$work/out/${safe_name%.storyboard}.storyboardc"
      ;;
    *.xib)
      output="$work/out/${safe_name%.xib}.nib"
      ;;
  esac

  printf 'Checking %s
' "$relative" | tee -a "$log"
  xcrun ibtool \
    --errors \
    --warnings \
    --notices \
    --minimum-deployment-target "$target" \
    --compile "$output" "$source" 2>&1 | tee -a "$log"
done

Le script utilise un répertoire temporaire et ne pollue donc pas le dépôt. En cas d’échec, conservez ibtool.log. En cas de réussite, les résultats compilés peuvent être supprimés et seul un résumé du journal doit être téléversé. N’ajoutez pas les sorties temporaires au gestionnaire de versions et ne laissez pas plusieurs tâches parallèles partager un répertoire fixe.

Distinguer le précontrôle autonome de la compilation du projet

L’appel autonome à ibtool permet de détecter un XML corrompu, certaines connexions défectueuses, des attributs incompatibles et des erreurs de compilation. Il ne connaît toutefois pas tous les réglages de compilation du target complet. Le module auquel appartient un contrôleur de vue personnalisé, l’inclusion d’une ressource dans Copy Bundle Resources et l’existence d’une classe après compilation conditionnelle doivent encore être vérifiés par xcodebuild.

Il est recommandé de répartir les contrôles en deux niveaux :

Niveau Entrées Principaux problèmes détectés Traitement en cas d’échec
Précontrôle rapide Storyboard, XIB Fichiers corrompus, attributs incompatibles, erreurs de connexion élémentaires Arrêt immédiat
Compilation du projet workspace, project, Scheme Résolution des modules, rattachement des ressources, contexte d’édition de liens et de signature Conservation du journal de compilation complet

Si le précontrôle autonome réussit, mais que la compilation du projet échoue, vérifiez en priorité Target Membership, le champ Module, l’historique de renommage des classes et l’héritage de la cible de déploiement. À l’inverse, si le précontrôle échoue déjà, il est inutile de poursuivre avec un archivage plus long.

Intégrer les fichiers localisés au même contrôle bloquant

La localisation des Storyboards prend généralement deux formes : des ressources distinctes pour chaque langue, ou Base Internationalization associée à des fichiers .strings. Dans le premier cas, les identifiants d’objets risquent de diverger ; dans le second, des clés supprimées peuvent subsister. Commencez par vérifier le format élémentaire des fichiers .strings avec plutil :

find . -name "*.strings" -print0 |
while IFS= read -r -d '' file; do
  plutil -lint "$file"
done

Pour un Base Storyboard, vous pouvez générer l’ensemble actuel des clés, puis le comparer aux fichiers localisés du dépôt :

mkdir -p "${TMPDIR:-/tmp}/ibtool-strings"
xcrun ibtool \
  --generate-strings-file "${TMPDIR:-/tmp}/ibtool-strings/Main.strings" \
  "App/Base.lproj/Main.storyboard"
plutil -lint "${TMPDIR:-/tmp}/ibtool-strings/Main.strings"

Le fichier généré ne sert qu’à la comparaison et ne doit pas remplacer directement les traductions. La méthode la plus sûre consiste à extraire les noms de clés, à les trier, puis à comparer les différences : les nouvelles clés alimentent la liste des éléments à traduire, les clés supprimées produisent une alerte de nettoyage, et le texte des clés existantes reste géré par le processus de localisation. Cette approche détecte les éléments manquants sans remplacer par erreur les traductions existantes par le texte Base.

Gérer la référence des avertissements et les faux positifs courants

Traiter systématiquement chaque sortie comme un échec peut sembler rigoureux à court terme, mais finit par habituer l’équipe à ignorer les indicateurs rouges. Une règle plus applicable consiste à faire échouer immédiatement les errors, à comparer les warnings à une référence validée et stockée dans le dépôt pour ne bloquer que les nouveaux, puis à conserver les notices dans les artefacts de compilation afin de les examiner régulièrement.

L’ordre de diagnostic habituel est le suivant :

  1. Vérifier que DEVELOPER_DIR correspond à celui de la compilation complète.
  2. Vérifier que IPHONEOS_DEPLOYMENT_TARGET provient bien des réglages réels du projet.
  3. Contrôler la casse des chemins de fichiers, en particulier pour les ressources dont le renommage n’a modifié que les majuscules et minuscules.
  4. Rechercher les outlets, actions et noms de classes personnalisées devenus invalides.
  5. Vérifier que les répertoires temporaires sont isolés par tâche afin d’empêcher les compilations parallèles d’écraser leurs sorties.
  6. Relancer avec le même Scheme une compilation sans signature afin de valider le contexte des modules.

La référence des avertissements doit conserver des identifiants stables et éviter les chemins absolus temporaires. Lors d’une mise à niveau de Xcode, exécutez d’abord le précontrôle dans une branche distincte, examinez les nouveaux diagnostics, puis actualisez la référence. Ne filtrez pas directement tous les nouveaux avertissements dans le commit de mise à niveau.

Mettre en place un déploiement progressif et réversible

Lors de la première intégration, vous pouvez vous contenter de collecter les journaux pendant une semaine sans bloquer les fusions. Après avoir identifié l’origine des faux positifs, configurez les errors déterministes comme bloquantes, puis activez progressivement le contrôle des nouveaux warnings. Le script lui-même doit être versionné dans le dépôt et partagé entre les Mac locaux et les Mac cloud, afin d’éviter qu’une autre implémentation ne reste dissimulée dans la configuration CI.

La liste de contrôle finale doit couvrir les points suivants : chaîne d’outils fixée ; exclusions explicites dans les répertoires analysés ; répertoire temporaire distinct pour chaque tâche ; cible de déploiement issue de la configuration du projet ; journaux conservés en pièces jointes en cas d’échec ; comparaison des clés de localisation sans remplacement ; exécution maintenue de la compilation complète du Scheme. Ainsi, les problèmes liés aux ressources d’interface apparaissent pendant un précontrôle de quelques dizaines de secondes, au lieu de provoquer à la fin de l’archivage une erreur difficile à reproduire.

Questions fréquentes

Le contrôle ibtool remplace-t-il une compilation Xcode complète ?

Non. Il accélère la validation des ressources d’interface, mais le Scheme réel doit toujours être compilé et testé.

Pourquoi un Storyboard valide localement échoue-t-il dans la CI ?

La CI peut utiliser une autre version de Xcode, une cible de déploiement différente, une casse de chemin incorrecte ou un module personnalisé indisponible.

Faut-il bloquer la CI sur tous les notices produits par ibtool ?

Non. Bloquez d’abord les errors, comparez les warnings à une base connue et conservez les notices dans les journaux.

VMMini M4

Exécutez votre prochaine tâche sur un nœud physique dédié

Choisissez le nœud et la période de facturation, puis commencez le déploiement avec la configuration fixe M4, 16 Go de RAM et 256 Go de SSD. La disponibilité réelle est indiquée en temps réel dans la console.

Louer un Mac cloud maintenant