Podmíněný překlad v mobilních aplikacích — podstata, direktivy a princip fungování

Autor: IT Sectr Publikováno: 2026-06-01 Doba čtení: 9 min

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 — technika selektivního překladu kódu podle podmínek platformy, konfigurace nebo verze jazyka.
  • Direktivy #if, #elseif, #else, #endif — základní konstrukce podmíněného překladu ve Swift, C, C++, Objective-C.
  • Kotlin nemá direktivy preprocesoru — místo nich se používají BuildConfig, expect/actual a sourceSets.
  • Výhoda — kód pro nevhodné platformy není překládán, což snižuje velikost binárního souboru a eliminuje chyby.
  • iOS/macOS sdílený kód — podmíněný překlad je základem vývoje multiplatformních frameworků Apple.

Co je podmíněný překlad

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í.

Podmíněný překlad ve Swift

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.

Kontroly platformy os()

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.

swift
// 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
    }
}

Kontroly verze překladače

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.

swift
// 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

Kontrola dostupnosti modulu canImport()

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ý.

swift
#if canImport(CoreHaptics)
    import CoreHaptics

    class HapticManager {
        private var engine: CHHapticEngine?

        func playTapFeedback() {
            guard let engine else { return }
            // Implementace hmatové odezvy
        }
    }
#endif

Alternativy v Kotlin a Android

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).

Source Sets v Gradle

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.

kotlin
// 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
    }
}

Expect/Actual v Kotlin Multiplatform

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í.

kotlin
// 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

Preprocesor C/C++ a NDK

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ý.

Příznaky platformy NDK

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.

cpp
// 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.

Praktické scénáře a antivzory

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.

Správné scénáře

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ářJazykPodmínka
Abstrakce platformySwift#if os(iOS)
LaděníSwift/ObjC#if DEBUG
Zpětná kompatibilitaSwift#if swift(>=5.7)
Nativní knihovnaC/C++#ifdef __ANDROID__
A/B testováníJava/KotlinBuildConfig.FLAVOR

Antivzory

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.

  • #if v každém souboru — architektonický antivzor. Kód platformy by měl být izolován za protokoly.
  • Vnořené #if — rychle se stávají nečitelnými. Hloubka vnoření by neměla přesáhnout 2 úrovně.
  • Duplikování celých funkcí — pokud je funkce zcela zkopírována v #if a #else, měla by být přesunuta do společné části.
  • Testování — kód uvnitř neaktivních větví není testován. Jsou nutná CI sestavení všech možných kombinací.
  • Magické příznaky — nedokumentované příznaky, o kterých nový tým vývojářů neví.

Často kladené otázky

Čím se liší podmíněný překlad od kontrol za běhu?

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í).

Lze použít #if uvnitř funkce ve Swift?

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.

Proč Kotlin nepřidal preprocesor?

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í.

Jak testovat kód uvnitř neaktivních větví #if?

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á.

Co se stane, pokud podmínka #if obsahuje syntaktickou chybu?

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í

  • Podmíněný překlad — technika překladu, která vylučuje necílený kód ve fázi sestavení, na rozdíl od kontrol za běhu.
  • Swift podporuje #if s os(), canImport(), swift() — direktivy bezpečné v čase překladu, vyžadující syntaktickou správnost všech větví.
  • Kotlin používá expect/actual a Gradle sourceSets místo preprocesoru — spolehlivější, ale méně flexibilní přístupy.
  • C/C++ v NDK používá klasický textový preprocesor #ifdef / #ifndef s platformovými makry __ANDROID__, __APPLE__.
  • Správné použití — abstrakce platformy, ladění, zpětná kompatibilita. Nesprávné — #if v každém souboru, hluboké vnoření, magické příznaky.
  • CI je povinný — všechny kombinace příznaků musí být automaticky sestavovány a testovány, jinak se kód v neaktivních větvích stane mrtvým.

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í.

Prodiskutovat projekt

Přečtěte si také