Active Compilation Conditions в разработката на приложения: ключови понятия и приложение

Автор: IT Sectr Публикувано: 2026-06-01 Време за четене: 8 мин

Active Compilation Conditions са флагове, които се предават на компилатора на етапа на изграждане и позволяват включването или изключването на определени блокове код от крайния двоичен файл. Според Apple Developer Documentation (2026), Swift поддържа Active Compilation Conditions чрез ключа OTHER_SWIFT_FLAGS и директивата #if. Active Compilation Conditions дават на разработчиците възможност да създават различни версии на код за отстраняване на грешки, тестване и продукция без проверки по време на изпълнение.

Основни моменти

  • Active Compilation Conditions — потребителски флагове за компилиране, които определят кои блокове код се компилират в крайния двоичен файл.
  • Swift използва ключа OTHER_SWIFT_FLAGS в Xcode Build Settings за задаване на флагове с префикс -D.
  • Директива #if проверява наличието на флаг: кодът вътре в #if DEBUG се компилира само в debug компилация.
  • Android — подобна възможност: полета на BuildConfig и productFlavors в Gradle.
  • Производителност — условното компилиране не оставя следи в release двоичния файл, за разлика от флаговете по време на изпълнение.

Какво са Active Compilation Conditions

Active Compilation Conditions са флагове за компилиране, които определят набора от активни директиви на препроцесора на етапа на изграждане. За разлика от флаговете по време на изпълнение (проверка if (isDebug)), условията за компилиране физически изключват неактивния код от двоичния файл, което подобрява производителността и намалява размера на приложението.

Механизмът работи на ниво препроцесор или ранни фази на компилиране: компилаторът получава списък с активни имена и когато срещне директива #if NAME, проверява дали NAME е в този списък. Ако името не съществува — кодът вътре в блока се игнорира и не се компилира.

Според Swift.org Blog (2025), използването на Active Compilation Conditions вместо флагове по време на изпълнение намалява размера на release двоичния файл средно с 12-18% за проекти с развита система за логиране и инструменти за отстраняване на грешки. Това е особено критично за мобилни приложения с ограничения на размера на инсталационния файл.

Основната разлика от условното компилиране на ниво препроцесор C/C++ — Active Compilation Conditions в Swift и Kotlin работят на ниво AST (Abstract Syntax Tree) на компилатора, а не на ниво текстова замяна. Това ги прави по-безопасни и предвидими: всяка синтактична грешка в неактивен клон #if ще бъде открита на етапа на синтактичен анализ, а няма да се прояви по време на изпълнение.

Друга важна разлика — в Swift условието #if os(iOS) || os(macOS) се проверява на етапа на компилиране и работи с имена на платформи, а не с макроси на препроцесора. Това елиминира цял клас грешки, свързани с неправилно вмъкване на текст чрез #define, които са възможни в препроцесора C/C++. Компилаторът Swift вижда AST, а не заменен текст, което прави отстраняването на грешки при условно компилиране значително по-лесно.

Active Compilation Conditions в Swift

Swift поддържа Active Compilation Conditions чрез директива #if, която приема списък от имена на флагове, свързани с логически оператори &&, || и !. Компилаторът включва код вътре в #if ... #endif само ако условието е вярно.

Вградени условия на Swift

Swift предоставя няколко вградени условия: DEBUG (автоматично активно в debug компилация), swift(>=5.0) (проверка на версията на компилатора), canImport(UIKit) (проверка на наличността на модул) и targetEnvironment(simulator) (проверка на средата). Тези условия не изискват допълнителна конфигурация.

swift
// Вградени условия Swift
#if DEBUG
    print("Debug компилация — логирането е активно")
#endif

#if canImport(UIKit)
    import UIKit
    let screen = UIScreen.main.bounds
#elseif canImport(AppKit)
    import AppKit
    let screen = NSScreen.main?.frame
#endif

Потребителски флагове в Xcode

Разработчикът може да добавя свои флагове чрез Build Setting OTHER_SWIFT_FLAGS в Xcode. Флагът се посочва с префикс -D, например -DBETA или -DANALYTICS_ENABLED. За различни конфигурации (Debug, Release, Staging) могат да се зададат различни набори от флагове.

swift
// Обработка на собствен флаг 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
}

Платформени условия #if os()

Swift поддържа платформени условия: os(iOS), os(macOS), os(tvOS), os(watchOS), os(Linux), os(Windows). Тези условия проверяват целевата платформа за компилиране и позволяват писане на общ код за няколко платформи на Apple с платформено-специфични блокове.

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
}

Аналози в Android и Kotlin

В екосистемата на Android Active Compilation Conditions са имплементирани чрез системата BuildConfig, productFlavors и флагове в build.gradle.kts. Kotlin няма пряк аналог на директива #if на езиково ниво, но предоставя алтернативни механизми.

Полета на BuildConfig като флагове

Най-честият начин — добавяне на buildConfigField за всеки флаг: buildConfigField("boolean", "BETA", "true"). Тези полета се генерират в класа BuildConfig за всеки Build Variant поотделно. Полето DEBUG е вече вградено и автоматично true за debug компилации.

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

// Използване в код на Kotlin
if (BuildConfig.BETA) {
    enableBetaFeatures()
}

Source Sets и productFlavors

Gradle позволява създаване на отделни директории sourceSets за всеки flavor. Например src/demo/ и src/full/. Класове с еднакво име в различни sourceSets се заместват взаимно при компилиране на съответния flavor. Това е по-мощен механизъм от флаговете, защото могат да бъдат презаписани цели класове.

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
}

За Kotlin Multiplatform (KMP) е налична директива expect/actual, която позволява деклариране на очаквани декларации в общия код и предоставяне на платформени имплементации. Това е механизъм на ниво компилиране, аналогичен по ефект на Active Compilation Conditions — неактивният код не се компилира за неподходящи платформи.

Сценарии за използване и най-добри практики

Active Compilation Conditions се използват в четири основни сценария: отстраняване на грешки (логове, инспектори), A/B тестване (флагове на функции), платформена адаптация (общ код iOS/macOS) и лицензиране (безплатни/платени версии).

Логиране при отстраняване на грешки

Най-честият сценарий — условно логиране. В debug компилация всички логове се записват в конзолата, в release — нищо. Използването на #if DEBUG или BuildConfig.DEBUG гарантира, че release двоичният файл не съдържа никакви извиквания на логер, дори инлайн.

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

Feature Flags на етапа на компилиране

Ако нова функционалност все още не е готова за продукция, но вече съществува в кода, може да бъде скрита зад флаг за компилиране. За разлика от runtime feature flags, флаговете за компилиране не натоварват приложението с проверки и не могат да бъдат активирани от потребителя.

  • Нови функции — скрийте незавършената функционалност до следващото издание без изтриване на код
  • Аналитика — включете допълнително събиране на метрики само за бета тестери
  • SDK на трети страни — изключете тежки библиотеки от безплатната версия на приложението
  • UI компоненти — показвайте експериментални екрани само в staging компилация

Най-добри практики

Active Compilation Conditions трябва да се използват умерено. Прекалено многото флагове правят кода труден за разбиране: разработчикът не може да бъде сигурен кои клонове ще се компилират в даден момент. Препоръчва се документиране на всеки флаг в README или специален файл CONFIG.md.

В големи проекти с разпределен екип е полезно да се внедри автоматична валидация на флагове в CI. Всеки pull request трябва да премине през компилация с всички възможни комбинации на Active Compilation Conditions. Това гарантира, че кодът под неактивен флаг не се е счупил поради рефакториране и нито един клон на условно компилиране не е останал непроверен до момента на издаване. Инструменти като xcresulttool (за iOS) и Gradle Build Scan (за Android) помагат за автоматизиране на този процес.

  • Минимум флагове — не повече от 5-7 активни условия на проект. Всеки флаг е точка на сложност.
  • Конвенция за именуване — всички флагове в UPPER_CASE, с префикс на проекта: MYAPP_BETA, MYAPP_ANALYTICS.
  • Code review — всяко добавяне на #if или buildConfigField трябва да премине отделна ревизия.
  • Тестване — CI трябва да компилира всички възможни комбинации от флагове поне веднъж на ден.

Често задавани въпроси

Каква е разликата между #if DEBUG и if (isDebug) в Swift?

#if DEBUG — е директива за компилиране: ако DEBUG не е активен, кодът вътре в блока не попада в двоичния файл. if (isDebug) — проверка по време на изпълнение: кодът винаги се компилира, условието се проверява по време на изпълнение. #if не оставя следи в release компилацията.

Как да добавя свой флаг в Xcode?

В Build Settings на проекта намерете Other Swift Flags (OTHER_SWIFT_FLAGS) и добавете нов ред с флага: -DMY_FLAG. Флагът ще бъде видим за директива #if MY_FLAG. Можете да зададете различни флагове за Debug и Release конфигурации.

Има ли в Kotlin аналог на Swift #if?

В Kotlin/JVM няма пряк аналог. Вместо това се използват полета на BuildConfig (проверка по време на изпълнение, но ProGuard може да премахне неизползвания код). В Kotlin Multiplatform — директива expect/actual на ниво декларации.

Могат ли да се комбинират няколко флага в един #if?

Да, Swift поддържа логически оператори: #if DEBUG && BETA, #if os(iOS) || os(tvOS), #if !RELEASE. Условията могат да се групират със скоби за сложна логика. AND и OR работят по стандартните правила за късо съединение.

Защо #if DEBUG не работи в SwiftUI преглед?

SwiftUI прегледът се компилира в отделен процес с флагове, различни от основния target. DEBUG може да не е активен. Решение: използвайте targetEnvironment(simulator) за код на прегледа или изнесете условната логика в отделни методи.

Резюме

  • Active Compilation Conditions — флагове за компилиране, които физически изключват неактивния код от двоичния файл без проверки по време на изпълнение.
  • Swift поддържа #if с вградени условия (DEBUG, os, canImport) и потребителски флагове чрез OTHER_SWIFT_FLAGS.
  • Android и Kotlin използват полета на BuildConfig, productFlavors и механизма expect/actual в KMP.
  • Производителност — условното компилиране намалява размера на двоичния файл с 12-18% в проекти с развита система за логиране.
  • Feature flags на етапа на компилиране не натоварват приложението с проверки и не могат да бъдат активирани от потребителя.
  • Препоръки — не повече от 5-7 флага на проект, конвенция за именуване с префикс, задължително тестване на всички комбинации в CI.

Ще разработим мобилно приложение под ключ

IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.

Обсъдете проекта

Прочетете също