Условно компилирање омогућава компилатору да укључи или прескочи дијелове изворног кода у зависности од услова познатих у фази изградње. Према The Swift Programming Language (2026), директива #if се обрађује у фази AST анализе прије генерисања машинског кода. Условно компилирање даје програмерима могућност одржавања јединствене базе кода за више платформи и конфигурација без дуплирања.
Главно
Условно компилирање — механизам у којем компилатор анализира директиве условног компилирања и укључује у излазни бинарни фајл само one блокове кода за које су услови испуњени. Ово омогућава постојање јединствене базе кода која се прилагођава различитим циљним платформама и конфигурацијама.
Концепт потиче из C/C++ са препроцесорским директивама #ifdef, #ifndef, #endif. У савременим језицима (Swift, Rust, Go) механизам ради на нивоу компилатора без посебног препроцесора, што повећава сигурност: условни блокови морају бити синтаксички исправни, чак и ако се не компилирају.
Према подацима Apple WWDC сесије „Embrace Swift” (2025), око 40% Swift пројеката користи условно компилирање за подршку iOS и macOS у једном target-у. За пројекте са UIKit и SwiftUI, UI код је често раздвојен директивама #if os(iOS) и #if os(macOS), што омогућава поновну употребу пословне логике.
Главна предност — сигурност у фази компилирања. Код за неодговарајућу платформу не само да се не извршава, већ се ни не компилира. То значи да се грешке у iOS-специфичном коду неће појавити приликом изградње за macOS и обрнуто. Провјере у фази извршавања не дају такву гаранцију.
Swift пружа четири кључне директиве: #if, #elseif, #else, #endif. За разлику од C препроцесора, Swift захтијева синтаксичку исправност кода у свим гранама — компилатор парсира сав код, али генерише машински код само за активне гране.
Swift подржава уграђене функције провјере: os(iOS), os(macOS), os(tvOS), os(watchOS), os(Linux), os(Windows). Ове функције провјеравају циљну платформу за коју се апликација изграђује. Комбинација са && и || омогућава стварање сложених услова.
// Јединствени код за iOS, macOS и tvOS
import Foundation
class PlatformService {
func getSystemVersion() -> String {
#if os(iOS) || os(tvOS)
return UIDevice.current.systemVersion
#elseif os(macOS)
let vers = ProcessInfo.processInfo.operatingSystemVersion
return "\(vers.majorVersion).\(vers.minorVersion)"
#else
return "unknown"
#endif
}
}
Swift подржава провјеру верзије компилатора: #if swift(>=5.9). Ово је корисно за библиотеке и оквире који подржавају више верзија Swift-а. Нове могућности језика (на примјер, макрои у Swift 5.9) могу бити заштићени таквом провјером.
// Уназад компатибилност
#if swift(>=5.9)
@MainActor
struct ModernView: View {
var body: some View {
Text("Модерни SwiftUI")
}
}
#else
struct ModernView: View {
var body: some View {
Text("Наслеђени SwiftUI")
}
}
#endif
Функција canImport(ModuleName) провјерава да ли је наведени модул доступан у тренутном окружењу изградње. Ово је најфлексибилнији механизам: није везан за одређену платформу. На примјер, код који користи CoreHaptics ће се компилирати само на уређајима на којима је овај оквир доступан.
#if canImport(CoreHaptics)
import CoreHaptics
class HapticManager {
private var engine: CHHapticEngine?
func playTapFeedback() {
guard let engine else { return }
// Имплементација тактилног одговора
}
}
#endif
Kotlin као језик нема препроцесорске директиве. Умјесто тога, Android екосистем нуди три алтернативе: BuildConfig поља (провјере у фази извршавања), sourceSets (замјена цијелих фајлова) и expect/actual (у Kotlin Multiplatform).
Gradle sourceSets омогућавају постојање различитих имплементација класа за различите варијанте или типове изградње. У директоријуму src/debug/ налази се имплементација за debug, у src/release/ — за release. Приликом изградње, Gradle бира одговарајући sourceSet и компилира само његове фајлове.
// src/debug/kotlin/com/example/Logger.kt
object Logger {
fun log(tag: String, message: String) {
Log.d(tag, message)
}
}
// src/release/kotlin/com/example/Logger.kt
object Logger {
fun log(tag: String, message: String) {
// No-op у release
}
}
KMP пружа механизам expect (декларација у заједничком коду) и actual (имплементација за одређену платформу). Ово је механизам у фази компилирања: за iOS се компилира actual имплементација из iOS sourceSet-а, за Android — из Android sourceSet-а. Нециљне имплементације се не компилирају.
// commonMain — expect декларација
expect fun getPlatformName(): String
// androidMain — actual за Android
actual fun getPlatformName(): String =
"Android \${Build.VERSION.SDK_INT}"
// iosMain — actual за iOS
actual fun getPlatformName(): String =
UIDevice.current.systemName() + " " + UIDevice.current.systemVersion
Приликом развоја изворних библиотека путем Android NDK користи се класични C/C++ препроцесор са директивама #ifdef, #ifndef, #define. За разлику од Swift-а, C препроцесор ради на текстуалном нивоу — код у неактивним гранама може бити синтаксички неисправан.
NDK дефинише макрое за сваку платформу: __ANDROID__ (Android), __APPLE__ (iOS/macOS), __linux__ (Linux). За архитектуре: __arm__, __aarch64__, __x86_64__. Ови макрои се аутоматски постављају од стране компилатора приликом изградње за циљну платформу.
// Изворни код за Android и iOS
#include <cstdint>
#ifdef __ANDROID__
int32_t getJniEnv(JNIEnv* env) {
return env->GetVersion();
}
#elif defined(__APPLE__)
#include <TargetConditionals.h>
int32_t getOsVersion() {
#if TARGET_OS_IOS
return "iOS";
#elif TARGET_OS_OSX
return "macOS";
#endif
}
#endif
При раду са NDK-ом важно је запамтити да је C/C++ препроцесор текстуална замјена. Ако у неактивној грани постоји синтаксичка грешка, компилатор је неће видјети, али ако се због неправилног #define поквари активна грана — грешка ће се појавити. Препоручује се минимизирање ланаца #define и коришћење constexpr константи.
За Rust, који се такође користи у мобилном развоју путем UniFFI и Mozilla Application Services, постоји сопствени механизам — feature flags у Cargo.toml. Заставице попут #[cfg(target_os = "android")] у Rust-у раде слично Swift директивама: провјера се врши на нивоу компилатора, а не препроцесора. Ово чини Rust атрактивним избором за изворне библиотеке које се морају компилирати за Android и iOS из једне базе кода.
Условно компилирање је ефикасно у строго одређеним сценаријима. При неправилној употреби ствара код са лошим мирисом који је тешко тестирати и одржавати. Размотримо исправне сценарије и типичне грешке.
Први сценариј — платформска апстракција: јединствени фасад, унутар којег условно компилирање бира платформску имплементацију. Други — отклањање грешака и профилисање: алати за програмере који не би требало да доспје у издање. Трећи — уназад компатибилност: подршка за старе верзије оперативног система док се не ажурира минимална верзија.
| Сценариј | Језик | Услов |
|---|---|---|
| Платформска апстракција | Swift | #if os(iOS) |
| Отклањање грешака | Swift/ObjC | #if DEBUG |
| Уназад компатибилност | Swift | #if swift(>=5.7) |
| Изворна библиотека | C/C++ | #ifdef __ANDROID__ |
| A/B тестирање | Java/Kotlin | BuildConfig.FLAVOR |
Најопаснији антиобразац — ширење директива по цијелом коду. Ако сваки други фајл садржи #if, то је сигнал да архитектура захтијева прераду. Исправно рјешење — издвојити платформски код иза протокола/интерфејса и користити Dependency Injection.
Често постављана питања
Условно компилирање ради у фази компилирања: неактивни код не доспијева у бинарни фајл. Провјере у фази извршавања (if / switch) се увијек компилирају, услов се провјерава током извршавања. Прво је сигурније и ефикасније, друго је флексибилније (може се мијењати без прекомпилирања).
Да, Swift дозвољава директиве #if унутар функција, петљи, па чак и унутар израза. Ово је једна од могућности која није постојала у раним верзијама Swift-а. На примјер: let x = #if DEBUG 1 #else 0 #endif — исправан код.
Програмери Kotlin-а су свјесно одустали од препроцесора, сматрајући га извором крхког кода. Умјесто тога нуде expect/actual (сигурност у фази компилирања) и Gradle sourceSets (изолација на нивоу фајлова). Оба приступа су поузданија од текстуалне замјене.
Изградити апликацију са различитим комбинацијама заставица у CI-ју. За Swift: подесити одвојене Xcode схеме са различитим Active Compilation Conditions. За Android: подесити одвојене Build Variants и покретати тестове за сваки. Аутоматизација је обавезна.
У Swift-у услов #if је компилаторска директива. Ако је сам услов синтаксички неисправан (на примјер, грешка у куцању назива os()), компилатор ће издати грешку компилирања. У C/C++ препроцесор једноставно неће пронаћи макро и услов ће постати лажан.
Резиме
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође