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 дают разработчикам возможность собирать разные версии кода для отладки, тестирования и продакшена без runtime-проверок.

Главное

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

Что такое Active Compilation Conditions

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, а не заменённый текст, что делает отладку условной компиляции значительно проще.

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("Отладочная сборка — логирование активно")
#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 для каждого флейвора. Например, src/demo/ и src/full/. Классы с одинаковым именем в разных sourceSets заменяют друг друга при сборке соответствующего флейвора. Это более мощный механизм, чем флаги, потому что можно переопределять целые классы.

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, компиляционные флаги не нагружают приложение проверками и не могут быть включены пользователем.

  • Новые фичи — скрыть незаконченный функционал до следующего релиза без удаления кода
  • Аналитика — подключить дополнительный сбор метрик только для beta-тестеров
  • Сторонние SDK — исключить тяжёлые библиотеки из бесплатной версии приложения
  • UI компоненты — показывать экспериментальные экраны только в staging-сборке

Лучшие практики

Active Compilation Conditions должны использоваться умеренно. Чрезмерное количество флагов делает код трудным для понимания: разработчик не может быть уверен, какие ветки скомпилируются в данный момент. Рекомендуется документировать каждый флаг в README или специальном файле CONFIG.md.

В крупных проектах с распределённой командой полезно внедрить автоматическую валидацию флагов в CI. Каждый pull request должен проходить сборку со всеми возможными комбинациями Active Compilation Conditions. Это гарантирует, что код под неактивным флагом не сломался из-за рефакторинга, и ни одна ветка conditional compilation не осталась непроверенной до момента релиза. Инструменты вроде 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) — runtime-проверка: код компилируется всегда, условие проверяется во время выполнения. #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 превью собираются в отдельном процессе с флагами, отличными от основного таргета. DEBUG может быть не активен. Решение: использовать targetEnvironment(simulator) для кода превью или выносить условную логику в отдельные методы.

Итоги

  • Active Compilation Conditions — флаги компиляции, физически исключающие неактивный код из бинарного файла без runtime-проверок.
  • 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 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также