Le fait qu’un App Clip puisse être lancé depuis un Scheme sur le Mac de développement ne garantit pas que le livrable soit correct. Les défauts les plus souvent ignorés ne sont pas des échecs de compilation : le Clip peut manquer dans l’archive hôte, les identifiants de l’application parente et du Clip peuvent être incohérents, ou les tests d’URL d’invocation peuvent se limiter au parcours idéal. En intégrant ces contrôles à une tâche d’intégration continue sur un Mac cloud, chaque commit est validé avec la même version de Xcode, les mêmes simulateurs et les mêmes scripts de recette.
Définir un livrable vérifiable
Ne considérez pas « Build Succeeded » comme le seul résultat attendu. Une validation complète doit produire au minimum l’archive .xcarchive de l’hôte, le journal de compilation séparé de l’App Clip, les fichiers d’entitlements analysés et un relevé des scénarios d’invocation. Il est préférable de conserver les URL de test dans la configuration du dépôt plutôt que de les disperser dans les réglages personnels des Schemes.
| Niveau de contrôle | Entrée | Condition de réussite |
|---|---|---|
| Compilation | App Clip Scheme | La cible peut être compilée pour le simulateur |
| Intégration | Archive hôte | Le Clip est présent sous AppClips |
| Association | Identifiants et entitlements des deux applications | La relation parent-enfant est cohérente |
| Invocation | URL de base, paramétrée et erronée | Le routage et le repli fonctionnent comme prévu |
Le lancement isolé du Clip valide le point d’entrée fonctionnel. Seule l’archivage de l’hôte suivi d’un contrôle de l’intégration valide la structure réellement livrable. Ces deux vérifications ne sont pas interchangeables.
Stabiliser le répertoire de compilation
La tâche cloud doit utiliser un DerivedData isolé afin que les artefacts d’une exécution précédente ne masquent pas une erreur survenue pendant la phase de copie. Commencez par compiler la version simulateur de l’App Clip, puis archivez l’hôte. En transmettant les noms des Schemes par des variables d’environnement, le même script peut être réutilisé sur différentes branches.
set -euo pipefail
ROOT="$PWD"
OUT="$ROOT/.ci-artifacts"
DERIVED="$OUT/DerivedData"
ARCHIVE="$OUT/HostApp.xcarchive"
rm -rf "$OUT"
mkdir -p "$OUT"
xcodebuild \
-workspace "$WORKSPACE" \
-scheme "$CLIP_SCHEME" \
-sdk iphonesimulator \
-destination "$SIMULATOR_DESTINATION" \
-derivedDataPath "$DERIVED" \
clean build | tee "$OUT/app-clip-build.log"
xcodebuild \
-workspace "$WORKSPACE" \
-scheme "$HOST_SCHEME" \
-destination "generic/platform=iOS" \
-archivePath "$ARCHIVE" \
archive | tee "$OUT/host-archive.log"
SIMULATOR_DESTINATION peut être fixé sur un type d’appareil et une version du système déjà installés dans le pipeline. Après une mise à niveau de Xcode, mettez d’abord à jour la tâche de référence ; un commit fonctionnel ordinaire ne doit pas modifier en même temps l’environnement d’exécution des tests.
Contrôler la relation entre les bundles de l’hôte et du Clip
Localiser l’artefact intégré
Une fois l’archivage terminé, recherchez l’application .app hôte dans Products/Applications, puis vérifiez que son répertoire AppClips contient exactement la cible attendue. Ne supposez pas que le nom du fichier d’application est fixe : recherchez-le par extension et contrôlez le nombre de résultats.
HOST_APP="$(find "$ARCHIVE/Products/Applications" -maxdepth 1 -name '*.app' -print -quit)"
CLIP_APP="$(find "$HOST_APP/AppClips" -maxdepth 1 -name '*.app' -print -quit)"
test -n "$HOST_APP"
test -n "$CLIP_APP"
HOST_ID="$(/usr/libexec/PlistBuddy -c 'Print :CFBundleIdentifier' "$HOST_APP/Info.plist")"
CLIP_ID="$(/usr/libexec/PlistBuddy -c 'Print :CFBundleIdentifier' "$CLIP_APP/Info.plist")"
printf '%s
' "$HOST_ID" > "$OUT/host-bundle-id.txt"
printf '%s
' "$CLIP_ID" > "$OUT/clip-bundle-id.txt"
Si le répertoire AppClips n’existe pas, contrôlez en priorité la phase d’intégration de la Target hôte, vérifiez que la Target du Clip appartient bien au Scheme actuellement archivé et assurez-vous que la configuration Release n’écrase pas des réglages valides en Debug.
Comparer les entitlements finaux
Les déclarations du fichier de projet ne correspondent pas nécessairement au résultat final présent dans l’artefact signé. Utilisez codesign -d --entitlements :- pour exporter séparément les entitlements de l’hôte et du Clip, puis convertissez-les dans un format stable avec plutil. Le contrôle bloquant doit vérifier que la valeur d’association à l’application parente du Clip désigne bien l’hôte courant, tout en interdisant les capacités qui ne doivent pas être héritées.
codesign -d --entitlements :- "$HOST_APP" > "$OUT/host-entitlements.plist"
codesign -d --entitlements :- "$CLIP_APP" > "$OUT/clip-entitlements.plist"
plutil -lint "$OUT/host-entitlements.plist"
plutil -lint "$OUT/clip-entitlements.plist"
Évitez de comparer uniquement le texte des fichiers complets, car une nouvelle version de Xcode peut modifier l’ordre des clés. Extrayez plutôt les clés qui doivent rester stables, comparez-les une par une et enregistrez leurs valeurs effectives dans les artefacts de compilation pour faciliter l’analyse après un échec.
Valider le routage avec trois types d’URL
Les tests d’invocation doivent couvrir au minimum le point d’entrée de base, des paramètres valides et des entrées incorrectes. Par exemple, le point d’entrée de base doit ouvrir le parcours léger par défaut ; une URL contenant l’identifiant d’une ressource doit afficher le contenu demandé ; si un paramètre est absent ou invalide, l’application doit revenir à une page sûre plutôt que de rester sur un écran vide.
Utiliser les URL comme données de test
L’utilisation de _XCAppClipURL dans le Scheme de l’App Clip convient au débogage manuel, mais l’intégration continue ne doit pas dépendre de l’état local du Scheme d’un développeur. Une approche plus robuste consiste à faire accepter un URL au parseur de routage, à couvrir directement les entrées avec des tests unitaires, puis à conserver une tâche de smoke test sur simulateur pour vérifier le cycle de vie de l’application.
func testInvocationRoutes() throws {
let base = try XCTUnwrap(URL(string: invocationBaseURL))
XCTAssertEqual(router.route(for: base), .home)
let item = try XCTUnwrap(URL(string: invocationItemURL))
XCTAssertEqual(router.route(for: item), .item(id: "42"))
let malformed = try XCTUnwrap(URL(string: invocationMalformedURL))
XCTAssertEqual(router.route(for: malformed), .fallback)
}
Cette organisation permet de séparer la résolution du domaine, la correspondance des chemins, la validation des paramètres de requête et le lancement de l’interface. Les tests unitaires de routage couvrent toutes les branches, tandis que la tâche sur simulateur vérifie uniquement que le processus réel démarre et affiche le premier écran. Le diagnostic des échecs devient ainsi plus direct.
Renforcer les contrôles et conserver l’état des échecs
En cas d’échec d’un contrôle bloquant, archivez au minimum les journaux xcodebuild, les deux fichiers Info.plist, les fichiers d’entitlements exportés, les identifiants de l’hôte et du Clip, ainsi que le nom du cas de test en échec. Si les journaux contiennent des jetons d’accès, des identifiants temporaires ou des segments sensibles de chemins locaux, masquez-les avant leur envoi.
Il est recommandé de fixer l’ordre des contrôles comme suit : nettoyer le répertoire de compilation isolé, compiler le Clip, archiver l’hôte, contrôler l’intégration, comparer les entitlements essentiels, exécuter les tests de routage, puis lancer le smoke test sur simulateur. Si un contrôle structurel préliminaire échoue, arrêtez immédiatement la tâche afin de ne pas consacrer du temps de simulateur à une archive incorrecte.
Les tâches exécutées sur GPUMini doivent également consigner explicitement xcodebuild -version, le SDK sélectionné et l’environnement d’exécution du simulateur. Ce qu’il faut maintenir dans la durée n’est pas la capture d’écran d’une réussite ponctuelle, mais un ensemble de contrôles aux entrées explicites, aux artefacts traçables et aux échecs rapidement reproductibles.
Questions fréquentes
Compiler uniquement la cible App Clip suffit-il ?
Non. Cette étape détecte les erreurs de compilation, mais pas une intégration absente, des identifiants incohérents ou des droits incorrects dans l’archive finale.
Une seule URL d’invocation est-elle suffisante pour le test ?
Non. Testez une route simple, une route avec paramètres et une entrée invalide afin de contrôler routage, décodage et repli sécurisé.
Pourquoi inspecter l’archive si le lancement local fonctionne ?
Un schéma local peut lancer directement le clip sans vérifier son intégration à l’application hôte. L’archive révèle les dérives de copie et de configuration.
Choisissez un Mac dans le cloud pour vos tâches de développement et de build
Comparez deux configurations M4, quatre durées de location et quatre nœuds disponibles à la vente, puis déployez selon vos besoins.