Active Compilation Conditions w tworzeniu aplikacji: kluczowe pojęcia i zastosowanie

Autor: IT Sectr Opublikowano: 2026-06-01 Czas czytania: 8 min

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 — niestandardowe flagi kompilacji określające, które bloki kodu są kompilowane do finalnego pliku binarnego.
  • Swift używa klucza OTHER_SWIFT_FLAGS w Xcode Build Settings do ustawiania flag z prefiksem -D.
  • Dyrektywa #if sprawdza obecność flagi: kod wewnątrz #if DEBUG jest kompilowany tylko w kompilacji debug.
  • Android — analogiczna możliwość: pola BuildConfig i productFlavors w Gradle.
  • Wydajność — kompilacja warunkowa nie pozostawia śladów w pliku binarnym release, w przeciwieństwie do flag runtime.

Czym są Active Compilation Conditions

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.

Active Compilation Conditions w Swift

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.

Wbudowane warunki Swift

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.

swift
// 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

Niestandardowe flagi w Xcode

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.

swift
// 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
}

Warunki platform #if os()

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.

swift
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
}

Odpowiedniki w Android i Kotlin

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.

Pola BuildConfig jako flagi

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.

kotlin
// build.gradle.kts
android {
    buildTypes {
        debug {
            buildConfigField("boolean", "BETA", "true")
        }
        release {
            buildConfigField("boolean", "BETA", "false")
        }
    }
}

// Użycie w kodzie Kotlin
if (BuildConfig.BETA) {
    enableBetaFeatures()
}

Source Sets i productFlavors

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.

kotlin
// 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.

Scenariusze użycia i najlepsze praktyki

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).

Logowanie debugowania

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.

swift
func logRequest(_ url: URL, _ statusCode: Int) {
    #if DEBUG
        Logger.debug("Request to \(url.absoluteString) returned \(statusCode)")
    #endif
}

Feature Flags na etapie kompilacji

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.

  • Nowe funkcje — ukryj niedokończoną funkcjonalność do następnej wersji bez usuwania kodu
  • Analityka — włącz dodatkowe zbieranie metryk tylko dla beta-testerów
  • SDK stron trzecich — wyklucz ciężkie biblioteki z darmowej wersji aplikacji
  • Komponenty UI — pokazuj eksperymentalne ekrany tylko w kompilacji staging

Najlepsze praktyki

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.

  • Minimum flag — nie więcej niż 5-7 aktywnych warunków na projekt. Każda flaga to punkt złożoności.
  • Konwencja nazewnictwa — wszystkie flagi w UPPER_CASE, z prefiksem projektu: MYAPP_BETA, MYAPP_ANALYTICS.
  • Code review — każde dodanie #if lub buildConfigField powinno przechodzić osobny przegląd.
  • Testowanie — CI powinien kompilować wszystkie możliwe kombinacje flag przynajmniej raz dziennie.

Często zadawane pytania

Jaka jest różnica między #if DEBUG a if (isDebug) w Swift?

#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.

Jak dodać własną flagę w Xcode?

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.

Czy w Kotlin istnieje odpowiednik Swift #if?

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.

Czy można łączyć wiele flag w jednym #if?

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.

Dlaczego #if DEBUG nie działa w podglądzie SwiftUI?

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

  • Active Compilation Conditions — flagi kompilacji fizycznie wykluczające nieaktywny kod z pliku binarnego bez sprawdzeń w czasie wykonania.
  • Swift obsługuje #if z wbudowanymi warunkami (DEBUG, os, canImport) i niestandardowymi flagami przez OTHER_SWIFT_FLAGS.
  • Android i Kotlin używają pól BuildConfig, productFlavors i mechanizmu expect/actual w KMP.
  • Wydajność — kompilacja warunkowa zmniejsza rozmiar pliku binarnego o 12-18% w projektach z rozbudowanym systemem logowania.
  • Feature flags na etapie kompilacji nie obciążają aplikacji sprawdzeniami i nie mogą być włączone przez użytkownika.
  • Zalecenia — nie więcej niż 5-7 flag na projekt, konwencja nazewnictwa z prefiksem, obowiązkowe testowanie wszystkich kombinacji w CI.

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.

Omów projekt

Przeczytaj również