Avatar ·

Замена #ifdef в Swift: как использовать условную компиляцию и флаги?

Привет! Я перехожу с Objective-C на Swift и столкнулся с проблемой. В Obj-C я часто использовал директивы препроцессора, например: ```objectivec #ifdef DEBUG NSLog(@"Отладочный лог"); #else NSLog(@"Релизная версия"); #endif ``` Или для проверки платформы: ```objectivec #if TARGET_OS_IOS // iOS код #elif TARGET_OS_MAC // macOS код #endif ``` В Swift я не могу найти прямой аналог #ifdef. Пытался написать что-то вроде: ```swift #if DEBUG print("Отладочный лог") #else print("Релизная версия") #endif ``` Но Xcode говорит, что `DEBUG` не определён. Я добавил `-D DEBUG` в Other Swift Flags, но всё равно не работает. Как правильно настроить условную компиляцию в Swift? Нужно ли что-то делать с Build Settings? И можно ли создавать свои флаги, например, для разных таргетов (lite/pro версии)? Буду благодарен за подробное объяснение с примерами! <<>>

Условная компиляция в Swift: полное руководство

В Swift действительно нет прямого аналога #ifdef, но есть более мощная и гибкая система условной компиляции, основанная на флагах конфигурации (configuration flags) и платформенных условиях.

1. Как работают условные блоки в Swift

Используйте конструкцию #if, #elseif, #else, #endif. Ключевое отличие от C-препроцессора: флаги в Swift — это не макросы, а логические константы времени компиляции.

// Пример для отладки
#if DEBUG
    print("⚠️ Режим отладки активен")
#else
    print("✅ Релизная сборка")
#endif

// Проверка платформы
#if os(iOS)
    import UIKit
    typealias Color = UIColor
#elseif os(macOS)
    import Cocoa
    typealias Color = NSColor
#endif

// Проверка архитектуры
#if arch(x86_64)
    print("Запуск на Intel-процессоре")
#elseif arch(arm64)
    print("Запуск на Apple Silicon")
#endif

2. Настройка флагов в Xcode

Для использования кастомных флагов (например, DEBUG) их нужно явно определить в настройках проекта:

  • Шаг 1: Выберите ваш таргет → Build Settings
  • Шаг 2: Найдите раздел Swift Compiler - Custom Flags
  • Шаг 3: В поле Active Compilation Conditions добавьте флаг (например, DEBUG) для Debug-конфигурации
  • Шаг 4: Для релизной конфигурации оставьте поле пустым или добавьте RELEASE

Важно: Xcode автоматически определяет флаг DEBUG для Debug-сборок, если вы добавили его в Active Compilation Conditions. Не путайте с Other Swift Flags (туда пишут -D префиксы, но это устаревший способ).

3. Кастомные флаги для разных таргетов

Допустим, у вас есть Lite и Pro версии приложения:

  • Для таргета MyAppLite в Active Compilation Conditions добавьте LITE_VERSION
  • Для таргета MyAppPro добавьте PRO_VERSION

В коде:

#if LITE_VERSION
    print("Lite версия: базовый функционал")
#elseif PRO_VERSION
    print("Pro версия: расширенные возможности")
#else
    print("Стандартная сборка")
#endif

4. Проверка доступности API (Swift 5.9+)

Swift также поддерживает проверку версий SDK и платформ:

// Доступно с iOS 15
if #available(iOS 15, *) {
    useNewAPI()
} else {
    useFallback()
}

// Условная компиляция по версии языка
#if swift(>=5.7)
    print("Используется Swift 5.7 или новее")
#endif

5. Типичные ошибки и их решение

  • Ошибка: "Use of unresolved identifier 'DEBUG'" — вы используете #if DEBUG без кавычек, но флаг не определён. Решение: добавьте DEBUG в Active Compilation Conditions.
  • Ошибка: "Fatal error: Unexpectedly found nil" — не путайте условную компиляцию с runtime-проверками. #if работает на этапе компиляции, а if — во время выполнения.
  • Совет: Для отладки используйте #if DEBUG, а не кастомные флаги — это стандарт в Swift-сообществе.

Заключение

Swift не поддерживает #ifdef, но предоставляет более гибкую систему через #if с флагами конфигурации. Основные инструменты: os(), arch(), swift(), canImport() и кастомные флаги в Active Compilation Conditions. Используйте их для разделения кода по платформам, версиям и конфигурациям сборки.

<<>>

Альтернативный подход: использование Build Configurations и Swift Compiler Flags

Если вы предпочитаете более традиционный C-стиль, можно использовать Other Swift Flags с префиксом -D. Это устаревший, но всё ещё работающий метод:

// В Build Settings → Other Swift Flags добавьте:
// Debug: -DDEBUG_MODE
// Release: -DRELEASE_MODE

// В коде:
#if DEBUG_MODE
    print("Отладка через флаги")
#elseif RELEASE_MODE
    print("Релиз через флаги")
#endif

Недостатки:

  • Меньшая читаемость (флаги выглядят как макросы)
  • Нет автоматического определения (как DEBUG)
  • Сложнее поддерживать в больших проектах

Рекомендация: Используйте Active Compilation Conditions — это современный и документированный способ. Для миграции со старых проектов просто перенесите все -D флаги в новое поле (без префикса -D).

Ещё один лайфхак: для проверки наличия модуля используйте canImport():

#if canImport(UIKit)
    // iOS/tvOS код
#elseif canImport(AppKit)
    // macOS код
#endif

Это особенно полезно для кросс-платформенных библиотек.