Un Scheme dans Xcode est une configuration qui définit comment compiler, tester, profiler et archiver une application pour iOS, macOS, watchOS ou tvOS. Chaque Scheme contient un ensemble d'actions (Build, Run, Test, Profile, Analyze, Archive) avec ses propres paramètres, arguments et variables d'environnement. Selon la documentation développeur d'Apple, 2025, Scheme est l'outil principal de gestion des configurations de compilation dans Xcode, remplaçant la commutation manuelle des paramètres. Xcode crée automatiquement un schéma pour chaque target à la première ouverture du projet.
L'essentiel
Scheme dans Xcode est un fichier XML (extension .xcscheme) qui décrit une séquence d'actions et leurs paramètres pour compiler et analyser une application. Chaque Scheme est lié à un ou plusieurs targets et définit avec quelle configuration (Debug, Release, AdHoc) exécuter chaque action. Scheme est l'équivalent du Build Variant sous Android, mais avec une structure plus flexible : un même schéma peut contenir différents targets pour différentes actions.
Xcode crée automatiquement un schéma pour chaque target à la première ouverture du projet. Le nom du schéma par défaut correspond au nom du target. Si le projet comporte un target de test, Xcode l'ajoute automatiquement à l'action Test du schéma du target principal. Pour les projets à plusieurs targets (application principale + watchOS + extension), Xcode crée un schéma séparé pour chacun, mais on peut aussi créer un seul schéma qui compile tous les targets d'un coup.
Les schémas sont stockés dans le répertoire xcshareddata/xcschemes/ (pour shared) ou xcuserdata/<user>/xcschemes/ (pour private). Les schémas shared vont dans Git et sont utilisés par toute l'équipe. Les schémas private sont stockés localement et ne sont pas synchronisés. Le fichier .xcscheme a un format XML avec l'élément racine <Scheme>. À l'intérieur se trouvent des blocs pour chaque action : BuildAction, TestAction, LaunchAction, ProfileAction, AnalyzeAction, ArchiveAction.
.xcscheme est un fichier XML qui peut être modifié manuellement ou via Xcode. Les principaux éléments : <BuildAction> (liste des targets à compiler), <TestAction> (liens vers les targets de test), <LaunchAction> (configuration de lancement), <ProfileAction>, <AnalyzeAction>, <ArchiveAction>. Chaque bloc contient l'attribut buildConfiguration qui détermine quelle configuration (Debug/Release) utiliser pour l'action concernée.
Scheme se compose de six actions, chacune configurable indépendamment. L'action Build détermine quels targets sont compilés et dans quel ordre. L'action Run détermine comment l'application est lancée : avec quels arguments, variables d'environnement et quelle configuration. L'action Test détermine quels tests sont exécutés et quelles options de couverture de code sont activées. L'action Profile lance avec les outils Instruments pour le profilage. L'action Analyze effectue l'analyse statique du code avec Clang Static Analyzer. L'action Archive compile pour la publication sur l'App Store ou la distribution AdHoc.
Pour chaque action, on peut définir une build configuration distincte. En général, on utilise Debug pour Run et Test, et Release pour Archive. La build configuration définit un ensemble de flags du compilateur, d'optimisations et d'informations de débogage. Xcode fournit deux configurations standard : Debug (sans optimisations, avec symboles de débogage) et Release (avec optimisations, sans informations de débogage). Le développeur peut ajouter des configurations personnalisées via project.xcconfig.
L'action Archive est particulièrement importante — elle crée un .xcarchive qui est ensuite exporté en .ipa pour l'App Store ou AdHoc. L'action Archive utilise la configuration Release par défaut, mais on peut passer à AdHoc ou Distribution. L'action Archive dispose aussi du flag revealArchiveInOrganizer : une fois l'archivage terminé, Xcode ouvre l'Organizer pour poursuivre le travail avec l'archive.
<!-- Exemple de .xcscheme pour une application iOS -->
<Scheme
LastUpgradeVersion = "1500"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "ABCD1234"
BuildableName = "MyApp.app"
BlueprintName = "MyApp"
ReferencedContainer = "container:MyApp.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
enableAddressSanitizer = "YES">
</LaunchAction>
</Scheme>
La création d'un nouveau schéma se fait via le menu de Xcode : Product → Scheme → New Scheme ou avec le bouton "+" du panneau Scheme (à côté du bouton Run). Lors de la création, on sélectionne le target pour lequel le schéma est créé. Xcode copie automatiquement les paramètres d'un schéma existant s'il est sélectionné comme "duplicate". Les nouveaux schémas sont enregistrés en private par défaut — pour les publier à l'équipe, il faut activer Shared dans Manage Schemes.
La fenêtre Edit Scheme (Product → Scheme → Edit Scheme) contient six onglets correspondant au nombre d'actions. Sur chaque onglet, on peut modifier la build configuration, les arguments de lancement, les variables d'environnement et les flags de diagnostic. L'onglet Run propose les options : executable (quel binaire lancer), wait for executable to be launched (pour déboguer les processus lancés), debugger (LLDB ou None), launch arguments, environment variables, et des options étendues (Address Sanitizer, Thread Sanitizer, Main Thread Checker, Memory Management).
Pour le diagnostic, Address Sanitizer (ASan) détecte les accès hors limites, use-after-free et autres erreurs mémoire dans le code C/C++/ObjC. Thread Sanitizer (TSan) détecte les courses de données (data races) dans le code multithread. Undefined Behavior Sanitizer (UBSan) détecte les comportements indéfinis, comme le dépassement d'un int signé. Ces options sont disponibles dans Edit Scheme → Run → Diagnostics et ne fonctionnent que pour les builds Debug. Activer tous les sanitizers peut ralentir le démarrage de 2 à 3 fois, il est donc recommandé de les activer de manière sélective.
Une pratique courante consiste à créer des schémas séparés pour chaque environnement : Dev, Staging, Production. Chaque schéma utilise la même Build Configuration (Debug pour Dev, Release pour Production), mais des arguments de lancement différents : -FIRAnalyticsDebugEnabled, -com.apple.CoreData.SQLDebug 1 pour Dev, et leur absence pour Production. Les arguments de lancement sont transmis à UserDefaults (ProcessInfo.processInfo.arguments) et sont disponibles à la lecture au démarrage de l'application. Cela permet de changer l'URL du serveur, le niveau de journalisation et les fonctionnalités sans modifier le code.
Les schémas shared sont stockés dans <project>.xcworkspace/xcshareddata/xcschemes/ ou <project>.xcodeproj/xcshareddata/xcschemes/ et vont dans le dépôt Git. Tous les développeurs de l'équipe voient ces schémas dans Xcode. Les schémas shared sont le seul moyen de distribuer des schémas au sein de l'équipe. Si un développeur a créé un schéma important (par exemple "Staging Archive") sans le marquer comme Shared, le reste de l'équipe ne le verra pas, ce qui crée de la confusion : chacun créera son propre schéma avec ses propres réglages.
Les schémas private sont stockés dans xcuserdata/<user>/xcschemes/ et ne vont pas dans Git. Ils sont utiles pour les configurations personnelles : par exemple, un schéma avec tous les sanitizers activés pour un développeur donné. Les schémas private ne doivent pas contenir de réglages critiques dont dépend la compilation du projet — si le développeur quitte le projet, ses schémas private disparaîtront. Recommandation : tous les schémas utilisés dans le CI/CD et par au moins deux développeurs doivent être Shared.
La gestion des schémas se fait via Manage Schemes (Product → Scheme → Manage Schemes). La fenêtre affiche tous les schémas du projet, leur statut (Shared/Private) et des boutons +/− pour ajouter/supprimer. La case Shared change la visibilité du schéma pour l'équipe. En cas de conflit Git (modifications de .xcscheme par deux développeurs), il faut résoudre le merge avec précaution — les fichiers XML peuvent contenir des identifiants de targets différents. Il est recommandé d'ajouter .xcscheme aux fichiers verrouillés lors du merge (git lfs ou .gitattributes).
Les arguments dans Scheme sont des chaînes transmises à l'application au lancement (ProcessInfo.processInfo.arguments) et des variables d'environnement (ProcessInfo.processInfo.environment). Les arguments servent pour les flags : -AppleLanguages (ru), -AppleLocale ru_RU pour simuler la locale russe, ou -FIRDebugEnabled pour activer le débogage Firebase. Les variables d'environnement servent à la configuration : API_BASE_URL=http://localhost:3000, LOG_LEVEL=debug.
Pour gérer les fonctionnalités (feature flags) dans différents environnements, on utilise une combinaison d'Arguments + Build Configuration. Dans le schéma Dev, on définit l'argument -FeatureFlagNewOnboarding YES, et dans Production — -FeatureFlagNewOnboarding NO (ou l'argument est absent). Dans le code, la vérification est : UserDefaults.standard.bool(forKey: "FeatureFlagNewOnboarding"). Cette approche permet d'activer progressivement des fonctionnalités sur staging sans modifier le code et sans committer les valeurs de production.
Important : les arguments et variables d'environnement du Scheme remplacent les valeurs d'Info.plist. Si API_URL est défini dans Info.plist et dans Scheme — API_URL=http://localhost pour l'action Run, au lancement depuis Xcode la valeur du Scheme sera utilisée. Au lancement sur un appareil (pas depuis Xcode) — la valeur d'Info.plist. C'est pratique pour le développement local, mais il faut se rappeler que les variables du Scheme n'entrent pas dans le build — elles n'agissent qu'au lancement via Xcode.
import Foundation
struct AppEnvironment {
var apiBaseURL: String {
ProcessInfo.processInfo.environment["API_BASE_URL"]
?? Bundle.main.object(forInfoDictionaryKey: "API_BASE_URL") as? String
?? "https://api.production.com"
}
var isDebugMode: Bool {
ProcessInfo.processInfo.arguments.contains("-DebugModeEnabled")
}
var isNewOnboardingEnabled: Bool {
UserDefaults.standard.bool(forKey: "FeatureFlagNewOnboarding")
}
}
// Utilisation au démarrage
let env = AppEnvironment()
NetworkConfig.shared.configure(baseURL: env.apiBaseURL)
Dans le CI/CD (GitHub Actions, Jenkins, GitLab CI), Scheme est utilisé comme argument principal de la commande xcodebuild. Exemple : xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Release -sdk iphoneos archive. Le flag -scheme indique quel schéma utiliser. xcodebuild lit tous les réglages du fichier .xcscheme, y compris la build configuration, les targets et l'ordre de compilation. Cela garantit que le CI/CD compile l'application avec les mêmes paramètres que l'IDE local.
Pour le CI/CD, les schémas shared sont essentiels. Si le schéma n'est pas Shared, xcodebuild ne le trouvera pas dans le dépôt et le build échouera avec l'erreur "Scheme not found". Règle : avant de configurer le CI/CD, assurez-vous que tous les schémas utilisés sont marqués Shared. Deuxième règle : dans le CI/CD, n'utilisez pas le schéma par défaut (Xcode sélectionne automatiquement le premier schéma) — transmettez toujours le nom du schéma explicitement via le flag -scheme.
Pour compiler plusieurs schémas en parallèle (par exemple, l'application et l'extension watchOS), on peut exécuter xcodebuild de manière séquentielle ou parallèle. Les systèmes CI modernes permettent de paralléliser la compilation de différents schémas via une matrice : un job compile l'application iOS, le second l'extension watchOS. Cela réduit le temps total de compilation de 15 à 8 minutes avec deux agents en parallèle. À la fin, les artefacts sont regroupés en un seul .xcarchive via xcodebuild -exportArchive.
#!/bin/bash — build CI/CD avec xcodebuild
# 1. Nettoyage et compilation
xcodebuild clean archive \
-workspace "MyApp.xcworkspace" \
-scheme "MyApp Production" \
-configuration Release \
-sdk iphoneos \
-archivePath "build/MyApp.xcarchive" \
CODE_SIGN_STYLE="Manual" \
PROVISIONING_PROFILE_SPECIFIER="match AppStore"
# 2. Exportation en IPA
xcodebuild -exportArchive \
-archivePath "build/MyApp.xcarchive" \
-exportPath "build/ipa" \
-exportOptionsPlist "ExportOptions.plist"
Questions fréquentes
En général, 2-3 schémas suffisent : Development (Debug), Staging (avec des arguments pour le serveur de test) et Production (Release). Pour les bibliothèques modulaires — un schéma avec des réglages de test. Ne multipliez pas les schémas : chaque nouveau schéma demande de la maintenance.
Build Configuration (Debug/Release) est un ensemble de flags du compilateur définis dans .xcconfig. Scheme est un ensemble d'actions, chacune faisant référence à une Build Configuration. Le schéma dit « au lancement, utilise Debug », la configuration définit « Debug signifie sans optimisations, avec symboles ».
Les arguments vont dans ProcessInfo.processInfo.arguments et UserDefaults (si l'argument commence par un tiret). Les variables d'environnement vont dans ProcessInfo.processInfo.environment. Dans le code : UserDefaults.standard.bool(forKey: "FeatureFlag") pour les arguments de la forme -FeatureFlag YES.
Oui, dans la Build Action, on peut ajouter plusieurs targets. Par exemple, un schéma "App + Watch + Widget" compilera les trois targets de manière séquentielle (si parallelizeBuildables=NO) ou parallèle (YES). Pour archiver l'application, le target principal suffit — les autres sont compilés comme dépendances.
Swift Package Manager ne remplace pas les schémas : le schéma définit toujours avec quelle configuration compiler les dépendances SPM, quels tests exécuter et comment archiver. Les paquets SPM peuvent avoir leurs propres schémas, qui sont importés automatiquement dans le projet lors de l'ajout du paquet.
Résumé
Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.
Lisez aussi