Scheme w Xcode to konfiguracja, która określa, jak budować, testować, profilować i archiwizować aplikację dla iOS, macOS, watchOS lub tvOS. Każda Scheme zawiera zestaw akcji (Build, Run, Test, Profile, Analyze, Archive) z własnymi parametrami, argumentami i zmiennymi środowiskowymi. Według Apple Developer Documentation, 2025 Scheme jest głównym narzędziem zarządzania konfiguracjami budowania w Xcode, zastępując ręczne przełączanie parametrów. Xcode automatycznie tworzy schemat dla każdego targetu przy pierwszym otwarciu projektu.
Najważniejsze
Scheme w Xcode to plik XML (rozszerzenie .xcscheme), który opisuje sekwencję akcji i ich parametry do budowania i analizy aplikacji. Każda Scheme jest powiązana z jednym lub kilkoma targetami i określa, z jaką konfiguracją (Debug, Release, AdHoc) wykonać każdą akcję. Scheme to odpowiednik Build Variant w Androidzie, ale o bardziej elastycznej strukturze: jeden schemat może zawierać różne targety dla różnych akcji.
Xcode automatycznie tworzy schemat dla każdego targetu przy pierwszym otwarciu projektu. Nazwa schematu domyślnie pokrywa się z nazwą targetu. Jeśli w projekcie jest target testowy, Xcode automatycznie dodaje go do akcji Test schematu głównego targetu. Dla projektów z wieloma targetami (główna aplikacja + watchOS + extension) Xcode tworzy osobny schemat dla każdego, ale można utworzyć jeden schemat, który buduje wszystkie targety naraz.
Schematy są przechowywane w katalogu xcshareddata/xcschemes/ (dla shared) lub xcuserdata/<user>/xcschemes/ (dla private). Schematy shared trafiają do Gita i są używane przez cały zespół. Schematy private są przechowywane lokalnie i nie są synchronizowane. Plik .xcscheme ma format XML z elementem głównym <Scheme>. Wewnątrz znajdują się bloki dla każdej akcji: BuildAction, TestAction, LaunchAction, ProfileAction, AnalyzeAction, ArchiveAction.
.xcscheme to plik XML, który można edytować ręcznie lub przez Xcode. Główne elementy: <BuildAction> (lista budowanych targetów), <TestAction> (odniesienia do targetów testowych), <LaunchAction> (konfiguracja uruchamiania), <ProfileAction>, <AnalyzeAction>, <ArchiveAction>. Każdy blok zawiera atrybut buildConfiguration, który określa, jaką konfigurację (Debug/Release) użyć dla danej akcji.
Scheme składa się z sześciu akcji, z których każdą można skonfigurować niezależnie. Akcja Build określa, które targety są budowane i w jakiej kolejności. Akcja Run — jak uruchamiana jest aplikacja: z jakimi argumentami, zmiennymi środowiskowymi i z jaką konfiguracją. Akcja Test — które testy są wykonywane i jakie opcje code coverage są włączone. Akcja Profile — uruchamianie z narzędziami Instruments do profilowania. Akcja Analyze — statyczna analiza kodu za pomocą Clang Static Analyzer. Akcja Archive — budowanie do publikacji w App Store lub dystrybucji AdHoc.
Dla każdej akcji można ustawić osobną build configuration. Zwykle dla Run i Test używa się Debug, a dla Archive — Release. Build configuration określa zestaw flag kompilatora, optymalizacje i informacje debugowania. Xcode udostępnia dwie standardowe konfiguracje: Debug (bez optymalizacji, z symbolami debugowania) i Release (z optymalizacjami, bez informacji debugowania). Deweloper może dodawać własne konfiguracje przez projekt.xcconfig.
Akcja Archive jest szczególnie ważna — tworzy .xcarchive, który następnie jest eksportowany do .ipa dla App Store lub AdHoc. Akcja Archive domyślnie używa konfiguracji Release, ale można przełączyć na AdHoc lub Distribution. W akcji Archive dostępna jest również flaga revealArchiveInOrganizer — po zakończeniu archiwizacji Xcode otwiera Organiser do dalszych działań z archiwum.
<!-- Przykład .xcscheme dla aplikacji 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>
Tworzenie nowego schematu odbywa się przez menu Xcode: Product → Scheme → New Scheme lub przyciskiem „+“ w panelu Scheme (obok przycisku Run). Podczas tworzenia wybierany jest target, dla którego tworzony jest schemat. Xcode automatycznie kopiuje ustawienia z istniejącego schematu, jeśli jest on wybrany jako „duplicate“. Nowe schematy domyślnie są zapisywane jako private — aby udostępnić je zespołowi, trzeba włączyć Shared w Manage Schemes.
Okno Edit Scheme (Product → Scheme → Edit Scheme) zawiera sześć zakładek odpowiadających liczbie akcji. Na każdej zakładce można zmienić build configuration, argumenty uruchomieniowe, zmienne środowiskowe i flagi diagnostyczne. Na zakładce Run dostępne są opcje: executable (który plik binarny uruchomić), wait for executable to be launched (do debugowania uruchamianych procesów), debugger (LLDB lub None), launch arguments, environment variables oraz opcje rozszerzone (Address Sanitizer, Thread Sanitizer, Main Thread Checker, Memory Management).
Do diagnostyki Address Sanitizer (ASan) — wykrywa wyjście poza granice tablicy, use-after-free i inne błędy pamięci w kodzie C/C++/ObjC. Thread Sanitizer (TSan) — wykrywa stany wyścigu (data races) w kodzie wielowątkowym. Undefined Behavior Sanitizer (UBSan) — wykrywa niezdefiniowane zachowanie, na przykład przepełnienie ze znakowego int. Te opcje są dostępne w Edit Scheme → Run → Diagnostics i działają tylko dla wersji Debug. Włączenie wszystkich sanitizerów może spowolnić uruchamianie 2-3 razy, dlatego zaleca się włączanie ich wybiórczo.
Typową praktyką jest tworzenie osobnych schematów dla każdego środowiska: Dev, Staging, Production. Każdy schemat używa tych samych Build Configuration (Debug dla Dev, Release dla Production), ale różnych argumentów uruchomieniowych: -FIRAnalyticsDebugEnabled, -com.apple.CoreData.SQLDebug 1 dla Dev i ich braku dla Production. Argumenty uruchomieniowe są przekazywane do UserDefaults (ProcessInfo.processInfo.arguments) i dostępne do odczytu przy starcie aplikacji. Pozwala to przełączać URL serwera, poziom logowania i funkcje bez zmiany kodu.
Schematy Shared są przechowywane w <project>.xcworkspace/xcshareddata/xcschemes/ lub <project>.xcodeproj/xcshareddata/xcschemes/ i trafiają do repozytorium Git. Wszyscy deweloperzy zespołu widzą te schematy w Xcode. Schematy Shared to jedyny sposób rozpowszechniania schematów w zespole. Jeśli deweloper utworzył ważny schemat (na przykład „Staging Archive“), ale nie oznaczył go jako Shared, reszta zespołu go nie zobaczy, co prowadzi do zamieszania: każdy będzie tworzył własny schemat z własnymi ustawieniami.
Schematy Private są przechowywane w xcuserdata/<user>/xcschemes/ i nie trafiają do Gita. Są przydatne do konfiguracji osobistych: na przykład schemat z włączonymi wszystkimi sanitizerami dla konkretnego dewelopera. Schematy private nie powinny zawierać krytycznych ustawień, od których zależy budowanie projektu — jeśli deweloper odejdzie z projektu, jego schematy private znikną. Zalecenie: wszystkie schematy używane w CI/CD i przez co najmniej dwóch deweloperów powinny być Shared.
Zarządzanie schematami odbywa się przez Manage Schemes (Product → Scheme → Manage Schemes). W oknie są wyświetlane wszystkie schematy projektu, ich status (Shared/Private) oraz przyciski +/— do dodawania/usuwania. Pole wyboru Shared przełącza widoczność schematu dla zespołu. Przy konflikcie Git (zmiany w .xcscheme od dwóch deweloperów) trzeba ostrożnie rozwiązywać scalanie — pliki XML mogą zawierać różne identyfikatory targetów. Zaleca się dodawanie .xcscheme do plików blokowanych przy merge (git lfs lub .gitattributes).
Arguments w Scheme to ciągi, które są przekazywane aplikacji przy uruchamianiu (ProcessInfo.processInfo.arguments) oraz zmienne środowiskowe (ProcessInfo.processInfo.environment). Argumenty służą do flag: -AppleLanguages (ru), -AppleLocale ru_RU do symulacji rosyjskiej lokalizacji lub -FIRDebugEnabled do włączenia debugowania Firebase. Zmienne środowiskowe są stosowane do konfiguracji: API_BASE_URL=http://localhost:3000, LOG_LEVEL=debug.
Do zarządzania funkcjami (feature flags) w różnych środowiskach używa się kombinacji Arguments + Build Configuration. W schemacie Dev ustawiany jest argument -FeatureFlagNewOnboarding YES, a w Production — -FeatureFlagNewOnboarding NO (lub argument nie występuje). W kodzie sprawdzenie: UserDefaults.standard.bool(forKey: "FeatureFlagNewOnboarding"). To podejście pozwala stopniowo włączać funkcje na staging bez zmiany kodu i bez commitowania wartości produkcyjnych.
Ważne: argumenty i zmienne środowiskowe Scheme nadpisują wartości z Info.plist. Jeśli w Info.plist podano API_URL, a w Scheme — API_URL=http://localhost dla Run Action, przy uruchomieniu z Xcode zostanie użyta wartość ze Scheme. Przy uruchomieniu z urządzenia (nie z Xcode) — wartość z Info.plist. To wygodne do lokalnego rozwoju, ale trzeba pamiętać, że zmienne Scheme nie trafiają do kompilacji — działają tylko przy uruchamianiu przez 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")
}
}
// Używane przy starcie
let env = AppEnvironment()
NetworkConfig.shared.configure(baseURL: env.apiBaseURL)
W CI/CD (GitHub Actions, Jenkins, GitLab CI) Scheme jest używana jako główny argument polecenia xcodebuild. Przykład: xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Release -sdk iphoneos archive. Flaga -scheme wskazuje, której schematu użyć. xcodebuild czyta wszystkie ustawienia z pliku .xcscheme, w tym build configuration, targety i kolejność budowania. To gwarantuje, że CI/CD buduje aplikację z tymi samymi parametrami co lokalne IDE.
Dla CI/CD krytyczne są schematy Shared. Jeśli schemat nie jest Shared, xcodebuild nie znajdzie go w repozytorium, a budowanie zakończy się błędem „Scheme not found“. Zasada: przed konfiguracją CI/CD upewnij się, że wszystkie używane schematy są oznaczone jako Shared. Druga zasada: w CI/CD nie używaj domyślnego schematu (Xcode automatycznie wybiera pierwszy schemat) — zawsze podawaj nazwę schematu jawnie przez flagę -scheme.
Do równoległego budowania kilku schematów (na przykład aplikacja i rozszerzenie watchOS) można uruchamiać xcodebuild sekwencyjnie lub równolegle. Nowoczesne systemy CI pozwalają zrównoleglić budowanie różnych schematów przez macierz: jedna praca buduje aplikację iOS, druga — rozszerzenie watchOS. To skraca całkowity czas budowania z 15 do 8 minut przy dwóch równoległych agentach. Na końcu artefakty są łączone w jeden .xcarchive za pomocą xcodebuild -exportArchive.
#!/bin/bash — CI/CD budowa z xcodebuild
# 1. Czyszczenie i budowanie
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. Eksport do IPA
xcodebuild -exportArchive \
-archivePath "build/MyApp.xcarchive" \
-exportPath "build/ipa" \
-exportOptionsPlist "ExportOptions.plist"
Często zadawane pytania
Zwykle wystarczą 2-3 schematy: Development (Debug), Staging (z argumentami dla serwera testowego) i Production (Release). Dla bibliotek modułowych — jeden schemat z ustawieniami do testowania. Nie mnoż schematów — każdy nowy schemat wymaga utrzymania.
Build Configuration (Debug/Release) — to zestaw flag kompilatora zdefiniowanych w .xcconfig. Scheme — to zestaw akcji, z których każda odwołuje się do Build Configuration. Schemat mówi „przy uruchamianiu użyj Debug“, konfiguracja określa „Debug — to bez optymalizacji, z symbolami“.
Argumenty trafiają do ProcessInfo.processInfo.arguments i UserDefaults (jeśli argument zaczyna się od myślnika). Zmienne środowiskowe — do ProcessInfo.processInfo.environment. W kodzie: UserDefaults.standard.bool(forKey: "FeatureFlag") dla argumentów postaci -FeatureFlag YES.
Tak, w Build Action można dodać wiele targetów. Na przykład schemat „App + Watch + Widget“ będzie budował wszystkie trzy targety sekwencyjnie (jeśli parallelizeBuildables=NO) lub równolegle (YES). Do archiwizacji aplikacji wystarczy główny target — pozostałe są budowane jako zależności.
Swift Package Manager nie zastępuje schematów — schemat nadal określa, z jaką konfiguracją budować zależności SPM, jakie testy uruchamiać i jak archiwizować. Pakiety SPM mogą mieć własne schematy, które są automatycznie importowane do projektu po dodaniu pakietu.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również