Podmíněný překlad umožňuje překladači zahrnout nebo přeskočit části zdrojového kódu v závislosti na podmínkách známých ve fázi sestavení. Podle The Swift Programming Language (2026) je direktiva #if zpracovávána ve fázi analýzy AST před generováním strojového kódu. Podmíněný překlad dává vývojářům možnost udržovat jednotnou kódovou základnu pro několik platforem a konfigurací bez duplikace.
Hlavní body
Podmíněný překlad je mechanismus, při kterém překladač analyzuje direktivy podmíněného překladu a zahrnuje do výstupního binárního souboru pouze ty bloky kódu, pro které jsou podmínky splněny. To umožňuje mít jednotnou kódovou základnu, která se přizpůsobuje různým cílovým platformám a konfiguracím.
Koncepce pochází z C/C++ s preprocesorovými direktivami #ifdef, #ifndef, #endif. V moderních jazycích (Swift, Rust, Go) mechanismus funguje na úrovni překladače bez samostatného preprocesoru, což zvyšuje bezpečnost: podmíněné bloky musí být syntakticky správné, i když nejsou překládány.
Podle údajů z relace Apple WWDC „Embrace Swift” (2025) používá přibližně 40% Swift projektů podmíněný překlad pro podporu iOS a macOS v jednom targetu. U projektů s UIKit a SwiftUI je UI kód často oddělen direktivami #if os(iOS) a #if os(macOS), což umožňuje opětovné použití obchodní logiky.
Hlavní výhoda — bezpečnost v čase překladu. Kód pro nevhodnou platformu se nejen nespouští, ale ani nepřekládá. To znamená, že chyby v kódu specifickém pro iOS se neprojeví při sestavování pro macOS a naopak. Kontroly za běhu takovou záruku neposkytují.
Swift poskytuje čtyři klíčové direktivy: #if, #elseif, #else, #endif. Na rozdíl od preprocesoru C Swift vyžaduje syntaktickou správnost kódu ve všech větvích — překladač analyzuje všechen kód, ale generuje strojový kód pouze pro aktivní větve.
Swift podporuje vestavěné kontrolní funkce: os(iOS), os(macOS), os(tvOS), os(watchOS), os(Linux), os(Windows). Tyto funkce kontrolují cílovou platformu, pro kterou je aplikace sestavována. Kombinace s && a || umožňuje vytvářet složité podmínky.
// Jednotný kód pro iOS, macOS a 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 podporuje kontrolu verze překladače: #if swift(>=5.9). To je užitečné pro knihovny a frameworky, které podporují několik verzí Swifta. Nové možnosti jazyka (například makra ve Swift 5.9) mohou být takovou kontrolou chráněny.
// Zpětná kompatibilita
#if swift(>=5.9)
@MainActor
struct ModernView: View {
var body: some View {
Text("Moderní SwiftUI")
}
}
#else
struct ModernView: View {
var body: some View {
Text("Starší SwiftUI")
}
}
#endif
Funkce canImport(ModuleName) kontroluje, zda je zadaný modul dostupný v aktuálním prostředí sestavení. Toto je nejflexibilnější mechanismus: není vázán na konkrétní platformu. Například kód používající CoreHaptics bude překládán pouze na zařízeních, kde je tento framework dostupný.
#if canImport(CoreHaptics)
import CoreHaptics
class HapticManager {
private var engine: CHHapticEngine?
func playTapFeedback() {
guard let engine else { return }
// Implementace hmatové odezvy
}
}
#endif
Kotlin jako jazyk nemá direktivy preprocesoru. Místo toho ekosystém Android nabízí tři alternativy: pole BuildConfig (kontroly za běhu), sourceSets (nahrazení celých souborů) a expect/actual (v Kotlin Multiplatform).
Gradle sourceSets umožňují mít různé implementace tříd pro různé příchutě nebo typy sestavení. V adresáři src/debug/ se nachází implementace pro debug, v src/release/ — pro release. Při sestavování Gradle vybere odpovídající sourceSet a přeloží pouze jeho soubory.
// 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 v release
}
}
KMP poskytuje mechanismus expect (deklarace ve sdíleném kódu) a actual (implementace pro konkrétní platformu). Jedná se o mechanismus v čase překladu: pro iOS se překládá actual implementace z iOS sourceSet, pro Android — z Android sourceSet. Necílené implementace se nepřekládají.
// commonMain — deklarace expect
expect fun getPlatformName(): String
// androidMain — actual pro Android
actual fun getPlatformName(): String =
"Android \${Build.VERSION.SDK_INT}"
// iosMain — actual pro iOS
actual fun getPlatformName(): String =
UIDevice.current.systemName() + " " + UIDevice.current.systemVersion
Při vývoji nativních knihoven prostřednictvím Android NDK se používá klasický preprocesor C/C++ s direktivami #ifdef, #ifndef, #define. Na rozdíl od Swifta pracuje C preprocesor na textové úrovni — kód v neaktivních větvích může být syntakticky nesprávný.
NDK definuje makra pro každou platformu: __ANDROID__ (Android), __APPLE__ (iOS/macOS), __linux__ (Linux). Pro architektury: __arm__, __aarch64__, __x86_64__. Tato makra jsou automaticky nastavována překladačem při sestavování pro cílovou platformu.
// Nativní kód pro Android a 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
Při práci s NDK je důležité pamatovat, že preprocesor C/C++ je textové nahrazení. Pokud je v neaktivní větvi syntaktická chyba, překladač ji neuvidí, ale pokud se kvůli nesprávnému #define pokazí aktivní větev — chyba se projeví. Doporučuje se minimalizovat řetězce #define a používat konstanty constexpr.
Pro Rust, který je také používán v mobilním vývoji prostřednictvím UniFFI a Mozilla Application Services, existuje vlastní mechanismus — feature flags v Cargo.toml. Příznaky jako #[cfg(target_os = "android")] v Rustu fungují podobně jako Swift direktivy: kontrola probíhá na úrovni překladače, nikoli preprocesoru. To činí Rust atraktivní volbou pro nativní knihovny, které musí být překládány pro Android a iOS z jedné kódové základny.
Podmíněný překlad je účinný v přísně definovaných scénářích. Při nesprávném použití vytváří kód se zápachem, který je obtížně testovatelný a udržovatelný. Podívejme se na správné scénáře a typické chyby.
První scénář — abstrakce platformy: jednotná fasáda, uvnitř které podmíněný překlad vybírá implementaci platformy. Druhý — ladění a profilování: vývojářské nástroje, které by se neměly dostat do vydání. Třetí — zpětná kompatibilita: podpora starých verzí operačního systému, dokud není aktualizována minimální verze.
| Scénář | Jazyk | Podmínka |
|---|---|---|
| Abstrakce platformy | Swift | #if os(iOS) |
| Ladění | Swift/ObjC | #if DEBUG |
| Zpětná kompatibilita | Swift | #if swift(>=5.7) |
| Nativní knihovna | C/C++ | #ifdef __ANDROID__ |
| A/B testování | Java/Kotlin | BuildConfig.FLAVOR |
Nejnebezpečnější antivzor — šíření direktiv po celém kódu. Pokud každý druhý soubor obsahuje #if, je to signál, že architektura vyžaduje refaktorování. Správným řešením je oddělit kód platformy za protokoly/rozhraní a použít Dependency Injection.
Často kladené otázky
Podmíněný překlad funguje ve fázi překladu: neaktivní kód se nedostane do binárního souboru. Kontroly za běhu (if / switch) jsou vždy překládány, podmínka je kontrolována během provádění. První je bezpečnější a účinnější, druhý je flexibilnější (lze měnit bez překompilování).
Ano, Swift povoluje direktivy #if uvnitř funkcí, cyklů a dokonce i uvnitř výrazů. To je jedna z možností, která chyběla v raných verzích Swifta. Například: let x = #if DEBUG 1 #else 0 #endif — správný kód.
Vývojáři Kotlinu vědomě upustili od preprocesoru, protože jej považují za zdroj křehkého kódu. Místo toho nabízejí expect/actual (bezpečnost v čase překladu) a Gradle sourceSets (izolace na úrovni souborů). Oba přístupy jsou spolehlivější než textové nahrazení.
Sestavujte aplikaci s různými kombinacemi příznaků v CI. Pro Swift: nastavte samostatná schémata Xcode s různými Active Compilation Conditions. Pro Android: nastavte samostatné Build Variants a spouštějte testy pro každý. Automatizace je povinná.
Ve Swift je podmínka #if direktivou překladače. Pokud je samotná podmínka syntakticky nesprávná (například překlep v názvu os()), překladač vydá chybu překladu. V C/C++ preprocesor jednoduše nenajde makro a podmínka se stane nepravdivou.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také