Active Compilation Conditions to flagi przekazywane kompilatorowi na etapie budowania, które pozwalają włączać lub wyłączać określone bloki kodu z finalnego pliku binarnego. Według Apple Developer Documentation (2026), Swift obsługuje Active Compilation Conditions poprzez klucz OTHER_SWIFT_FLAGS i dyrektywę #if. Active Compilation Conditions dają programistom możliwość budowania różnych wersji kodu do debugowania, testowania i produkcji bez sprawdzeń w czasie wykonania.
Najważniejsze
Active Compilation Conditions to flagi kompilacji, które określają zestaw aktywnych dyrektyw preprocesora na etapie budowania. W przeciwieństwie do flag runtime (sprawdzenie if (isDebug)), warunki kompilacji fizycznie wykluczają nieaktywny kod z pliku binarnego, co zwiększa wydajność i zmniejsza rozmiar aplikacji.
Mechanizm działa na poziomie preprocesora lub wczesnych faz kompilacji: kompilator otrzymuje listę aktywnych nazw, a gdy napotka dyrektywę #if NAME, sprawdza czy NAME znajduje się na tej liście. Jeśli nazwy nie ma — kod wewnątrz bloku jest ignorowany i nie kompilowany.
Według Swift.org Blog (2025), użycie Active Compilation Conditions zamiast flag runtime zmniejsza rozmiar pliku binarnego release średnio o 12-18% w projektach z rozbudowanym systemem logowania i narzędziami debugowania. Jest to szczególnie istotne dla aplikacji mobilnych z ograniczeniami rozmiaru pliku instalacyjnego.
Główna różnica w stosunku do kompilacji warunkowej na poziomie preprocesora C/C++ — Active Compilation Conditions w Swift i Kotlin działają na poziomie kompilatora AST (Abstract Syntax Tree), a nie na poziomie zastępowania tekstu. To czyni je bezpieczniejszymi i bardziej przewidywalnymi: każdy błąd składniowy w nieaktywnej gałęzi #if zostanie wykryty na etapie parsowania, a nie ujawni się w czasie wykonania.
Kolejną ważną różnicą jest to, że w Swift warunek #if os(iOS) || os(macOS) jest sprawdzany na etapie kompilacji i działa z nazwami platform, a nie z makrami preprocesora. To eliminuje całą klasę błędów związanych z nieprawidłowym wstawianiem tekstu przez #define, które są możliwe w preprocesorze C/C++. Kompilator Swift widzi AST, a nie zastąpiony tekst, co znacznie ułatwia debugowanie kompilacji warunkowej.
Swift obsługuje Active Compilation Conditions poprzez dyrektywę #if, która przyjmuje listę nazw flag połączonych operatorami logicznymi &&, || i !. Kompilator włącza kod wewnątrz #if ... #endif tylko jeśli warunek jest prawdziwy.
Swift udostępnia kilka wbudowanych warunków: DEBUG (automatycznie aktywny w kompilacji debug), swift(>=5.0) (sprawdzenie wersji kompilatora), canImport(UIKit) (sprawdzenie dostępności modułu) oraz targetEnvironment(simulator) (sprawdzenie środowiska). Te warunki nie wymagają dodatkowej konfiguracji.
// Wbudowane warunki Swift
#if DEBUG
print("Kompilacja debug — logowanie aktywne")
#endif
#if canImport(UIKit)
import UIKit
let screen = UIScreen.main.bounds
#elseif canImport(AppKit)
import AppKit
let screen = NSScreen.main?.frame
#endif
Programista może dodawać własne flagi poprzez Build Setting OTHER_SWIFT_FLAGS w Xcode. Flaga jest podawana z prefiksem -D, na przykład -DBETA lub -DANALYTICS_ENABLED. Dla różnych konfiguracji (Debug, Release, Staging) można ustawić różne zestawy flag.
// Obsługa własnej flagi BETA
#if BETA
let apiEndpoint = "https://beta.api.com"
let isLoggingEnabled = true
#else
let apiEndpoint = "https://api.com"
let isLoggingEnabled = false
#endif
func trackEvent(_ name: String) {
#if ANALYTICS_ENABLED
Analytics.log(name)
#endif
}
Swift obsługuje warunki platform: os(iOS), os(macOS), os(tvOS), os(watchOS), os(Linux), os(Windows). Te warunki sprawdzają docelową platformę kompilacji i pozwalają pisać kod wspólny dla wielu platform Apple z blokami specyficznymi dla platformy.
import Foundation
func getDeviceName() -> String {
#if os(iOS)
return UIDevice.current.name
#elseif os(macOS)
return Host.current.name ?? "Unknown"
#else
return "Other platform"
#endif
}
W ekosystemie Android Active Compilation Conditions są zaimplementowane poprzez system BuildConfig, productFlavors i flagi w build.gradle.kts. Kotlin nie ma bezpośredniego odpowiednika dyrektywy #if na poziomie języka, ale udostępnia alternatywne mechanizmy.
Najczęstszy sposób — dodać buildConfigField dla każdej flagi: buildConfigField("boolean", "BETA", "true"). Te pola są generowane w klasie BuildConfig dla każdego Build Variant osobno. Pole DEBUG jest już wbudowane i automatycznie true dla kompilacji debug.
// build.gradle.kts
android {
buildTypes {
debug {
buildConfigField("boolean", "BETA", "true")
}
release {
buildConfigField("boolean", "BETA", "false")
}
}
}
// Użycie w kodzie Kotlin
if (BuildConfig.BETA) {
enableBetaFeatures()
}
Gradle pozwala tworzyć oddzielne katalogi sourceSets dla każdego flavora. Na przykład src/demo/ i src/full/. Klasy o tej samej nazwie w różnych sourceSets zastępują się nawzajem podczas kompilacji odpowiedniego flavora. To potężniejszy mechanizm niż flagi, ponieważ można nadpisywać całe klasy.
// src/demo/java/com/example/Config.kt
object Config {
const val API_URL = "http://demo.api.com"
const val IS_BETA = true
}
// src/full/java/com/example/Config.kt
object Config {
const val API_URL = "https://full.api.com"
const val IS_BETA = false
}
Dla Kotlin Multiplatform (KMP) dostępna jest dyrektywa expect/actual, która pozwala deklarować oczekiwane deklaracje w kodzie wspólnym i dostarczać implementacje platformowe. To mechanizm na poziomie kompilacji, analogiczny do Active Compilation Conditions pod względem efektu — nieaktywny kod nie jest kompilowany dla nieodpowiednich platform.
Active Compilation Conditions są stosowane w czterech głównych scenariuszach: debugowanie (logi, inspektory), testowanie A/B (flagi funkcji), adaptacja platformowa (wspólny kod iOS/macOS) oraz licencjonowanie (wersje darmowe/płatne).
Najczęstszy scenariusz — warunkowe logowanie. W kompilacji debug wszystkie logi są zapisywane do konsoli, w release — żadne. Użycie #if DEBUG lub BuildConfig.DEBUG gwarantuje, że plik binarny release nie zawiera żadnego wywołania loggera, nawet inline.
func logRequest(_ url: URL, _ statusCode: Int) {
#if DEBUG
Logger.debug("Request to \(url.absoluteString) returned \(statusCode)")
#endif
}
Jeśli nowa funkcjonalność nie jest jeszcze gotowa do produkcji, ale już istnieje w kodzie, można ją ukryć za flagą kompilacji. W przeciwieństwie do runtime feature flags, flagi kompilacji nie obciążają aplikacji sprawdzeniami i nie mogą być włączone przez użytkownika.
Active Compilation Conditions powinny być używane umiarkowanie. Nadmierna liczba flag utrudnia zrozumienie kodu: programista nie może być pewien, które gałęzie zostaną skompilowane w danym momencie. Zaleca się dokumentowanie każdej flagi w README lub specjalnym pliku CONFIG.md.
W dużych projektach z rozproszonym zespołem warto wdrożyć automatyczną walidację flag w CI. Każdy pull request powinien przechodzić kompilację ze wszystkimi możliwymi kombinacjami Active Compilation Conditions. Gwarantuje to, że kod pod nieaktywną flagą nie został zepsuty przez refaktoryzację i żadna gałąź kompilacji warunkowej nie pozostała niesprawdzona do momentu wydania. Narzędzia takie jak xcresulttool (dla iOS) i Gradle Build Scan (dla Android) pomagają zautomatyzować ten proces.
Często zadawane pytania
#if DEBUG — to dyrektywa kompilacji: jeśli DEBUG nie jest aktywny, kod wewnątrz bloku nie trafia do pliku binarnego. if (isDebug) — sprawdzenie w czasie wykonania: kod jest zawsze kompilowany, warunek sprawdzany podczas wykonania. #if nie pozostawia śladów w kompilacji release.
W Build Settings projektu znaleźć Other Swift Flags (OTHER_SWIFT_FLAGS) i dodać nowy wiersz z flagą: -DMY_FLAG. Flaga będzie widoczna dla dyrektywy #if MY_FLAG. Można ustawić różne flagi dla konfiguracji Debug i Release.
W Kotlin/JVM nie ma bezpośredniego odpowiednika. Zamiast tego używa się pól BuildConfig (sprawdzenie w czasie wykonania, ale ProGuard może usunąć nieużywany kod). W Kotlin Multiplatform — dyrektywa expect/actual na poziomie deklaracji.
Tak, Swift obsługuje operatory logiczne: #if DEBUG && BETA, #if os(iOS) || os(tvOS), #if !RELEASE. Warunki można grupować nawiasami dla złożonej logiki. AND i OR działają według standardowych zasad zwarcia.
Podgląd SwiftUI jest kompilowany w oddzielnym procesie z flagami różniącymi się od głównego targetu. DEBUG może nie być aktywny. Rozwiązanie: użyć targetEnvironment(simulator) dla kodu podglądu lub przenieść warunkową logikę do osobnych metod.
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ż