Active Compilation Conditions — это флаги, которые передаются компилятору на этапе сборки и позволяют включать или исключать определённые блоки кода из финального бинарного файла. По данным Apple Developer Documentation (2026), Swift поддерживает Active Compilation Conditions через ключ OTHER_SWIFT_FLAGS и директиву #if. Active Compilation Conditions дают разработчикам возможность собирать разные версии кода для отладки, тестирования и продакшена без runtime-проверок.
Главное
Active Compilation Conditions — это флаги компиляции, которые определяют набор активных директив препроцессора на этапе сборки. В отличие от runtime-флагов (проверка if (isDebug)), компиляционные условия физически исключают неактивный код из бинарного файла, что даёт прирост производительности и уменьшает размер приложения.
Механизм работает на уровне препроцессора или ранних фаз компиляции: компилятор получает список активных имён, и при встрече директивы #if NAME проверяет, есть ли NAME в этом списке. Если имени нет — код внутри блока игнорируется и не компилируется.
По данным Swift.org Blog (2025), использование Active Compilation Conditions вместо runtime-флагов сокращает размер 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, а не заменённый текст, что делает отладку условной компиляции значительно проще.
Swift поддерживает Active Compilation Conditions через директиву #if, которая принимает список имён флагов, объединённых логическими операторами &&, || и !. Компилятор включает код внутри #if ... #endif только если условие истинно.
Swift предоставляет несколько встроенных условий: DEBUG (автоматически активен в debug-сборке), swift(>=5.0) (проверка версии компилятора), canImport(UIKit) (проверка доступности модуля) и targetEnvironment(simulator) (проверка окружения). Эти условия не требуют дополнительной настройки.
// Встроенные условия Swift
#if DEBUG
print("Отладочная сборка — логирование активно")
#endif
#if canImport(UIKit)
import UIKit
let screen = UIScreen.main.bounds
#elseif canImport(AppKit)
import AppKit
let screen = NSScreen.main?.frame
#endif
Разработчик может добавлять свои флаги через Build Setting OTHER_SWIFT_FLAGS в Xcode. Флаг указывается с префиксом -D, например -DBETA или -DANALYTICS_ENABLED. Для разных конфигураций (Debug, Release, Staging) можно установить разные наборы флагов.
// Обработка своего флага 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 поддерживает платформенные условия: os(iOS), os(macOS), os(tvOS), os(watchOS), os(Linux), os(Windows). Эти условия проверяют целевую платформу сборки и позволяют писать код, общий для нескольких платформ Apple, с платформо-специфичными блоками.
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 экосистеме Active Compilation Conditions реализованы через систему BuildConfig, productFlavors и флаги в build.gradle.kts. Kotlin не имеет прямого аналога директивы #if на уровне языка, но предоставляет альтернативные механизмы.
Самый распространённый способ — добавить buildConfigField для каждого флага: buildConfigField("boolean", "BETA", "true"). Эти поля генерируются в класс BuildConfig для каждого Build Variant отдельно. Поле DEBUG уже встроено и автоматически true для debug-сборок.
// build.gradle.kts
android {
buildTypes {
debug {
buildConfigField("boolean", "BETA", "true")
}
release {
buildConfigField("boolean", "BETA", "false")
}
}
}
// Использование в коде Kotlin
if (BuildConfig.BETA) {
enableBetaFeatures()
}
Gradle позволяет создавать отдельные директории sourceSets для каждого флейвора. Например, src/demo/ и src/full/. Классы с одинаковым именем в разных sourceSets заменяют друг друга при сборке соответствующего флейвора. Это более мощный механизм, чем флаги, потому что можно переопределять целые классы.
// 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-бинарник не содержит ни одного вызова логгера, даже заинлайненного.
func logRequest(_ url: URL, _ statusCode: Int) {
#if DEBUG
Logger.debug("Request to \(url.absoluteString) returned \(statusCode)")
#endif
}
Если новая функциональность ещё не готова для продакшена, но уже есть в коде, её можно скрыть за флагом компиляции. В отличие от runtime feature flags, компиляционные флаги не нагружают приложение проверками и не могут быть включены пользователем.
Active Compilation Conditions должны использоваться умеренно. Чрезмерное количество флагов делает код трудным для понимания: разработчик не может быть уверен, какие ветки скомпилируются в данный момент. Рекомендуется документировать каждый флаг в README или специальном файле CONFIG.md.
В крупных проектах с распределённой командой полезно внедрить автоматическую валидацию флагов в CI. Каждый pull request должен проходить сборку со всеми возможными комбинациями Active Compilation Conditions. Это гарантирует, что код под неактивным флагом не сломался из-за рефакторинга, и ни одна ветка conditional compilation не осталась непроверенной до момента релиза. Инструменты вроде xcresulttool (для iOS) и Gradle Build Scan (для Android) помогают автоматизировать этот процесс.
Часто задаваемые вопросы
#if DEBUG — это директива компиляции: если DEBUG не активен, код внутри блока не попадает в бинарник. if (isDebug) — runtime-проверка: код компилируется всегда, условие проверяется во время выполнения. #if не оставляет следов в release-сборке.
В Build Settings проекта найти Other Swift Flags (OTHER_SWIFT_FLAGS) и добавить новую строку с флагом: -DMY_FLAG. Флаг будет виден директиве #if MY_FLAG. Можно установить разные флаги для Debug и Release конфигураций.
В Kotlin/JVM нет прямого аналога. Вместо этого используются BuildConfig поля (проверка во время выполнения, но ProGuard может выкинуть неиспользуемый код). В Kotlin Multiplatform — директива expect/actual на уровне объявлений.
Да, Swift поддерживает логические операторы: #if DEBUG && BETA, #if os(iOS) || os(tvOS), #if !RELEASE. Условия можно группировать скобками для сложной логики. AND и OR работают по стандартным правилам короткого замыкания.
SwiftUI превью собираются в отдельном процессе с флагами, отличными от основного таргета. DEBUG может быть не активен. Решение: использовать targetEnvironment(simulator) для кода превью или выносить условную логику в отдельные методы.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также