Conditional Compilation, derleyicinin derleme zamanında bilinen koşullara bağlı olarak kaynak kodunun bazı kısımlarını dahil etmesine veya atlamasına olanak tanır. The Swift Programming Language (2026)'ya göre, #if direktifi makine kodu oluşturulmadan önce AST analizi sırasında işlenir. Conditional Compilation, geliştiricilere tekrarlama olmadan birden fazla platform ve yapılandırma için tek bir kod tabanını sürdürmelerini sağlar.
Ana noktalar
Conditional Compilation, derleyicinin koşullu derleme direktiflerini analiz ettiği ve koşulları karşılanan kod bloklarını çıktı ikili dosyasına dahil ettiği bir mekanizmadır. Bu, farklı hedef platformlara ve yapılandırmalara uyum sağlayan tek bir kod tabanına sahip olmayı sağlar.
Kavram, #ifdef, #ifndef, #endif ön işlemci direktifleriyle C/C++'tan gelir. Modern dillerde (Swift, Rust, Go), mekanizma ayrı bir ön işlemci olmadan derleyici seviyesinde çalışır ve güvenliği artırır: koşullu bloklar derlenmeseler bile sözdizimsel olarak doğru olmalıdır.
Apple WWDC oturumu “Embrace Swift” (2025)'e göre, Swift projelerinin yaklaşık %40'ı tek bir hedefte iOS ve macOS'u desteklemek için koşullu derleme kullanır. UIKit ve SwiftUI kullanan projelerde, UI kodu genellikle #if os(iOS) ve #if os(macOS) direktifleriyle bölünür ve iş mantığının yeniden kullanılmasına olanak tanır.
Ana avantaj derleme zamanı güvenliğidir. Uygun olmayan bir platform için kod yalnızca çalıştırılmaz, aynı zamanda derlenmez. Bu, iOS'a özgü koddaki hataların macOS için derlerken ortaya çıkmayacağı ve bunun tersi anlamına gelir. Çalışma zamanı kontrolleri bu tür garantiler sağlamaz.
Swift dört ana direktif sağlar: #if, #elseif, #else, #endif. C ön işlemcisinin aksine Swift, tüm dallarda kodun sözdizimsel doğruluğunu gerektirir — derleyici tüm kodu ayrıştırır ancak yalnızca aktif dallar için makine kodu üretir.
Swift yerleşik kontrol fonksiyonlarını destekler: os(iOS), os(macOS), os(tvOS), os(watchOS), os(Linux), os(Windows). Bu fonksiyonlar, uygulamanın derlendiği hedef platformu kontrol eder. && ve || ile kombinasyon, karmaşık koşullar oluşturmayı sağlar.
// iOS, macOS ve tvOS için tek kod
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 derleyici sürümü kontrolünü destekler: #if swift(>=5.9). Bu, birden fazla Swift sürümünü destekleyen kütüphaneler ve framework'ler için kullanışlıdır. Yeni dil özellikleri (Swift 5.9'daki makrolar gibi) böyle bir kontrollerle korunabilir.
// Geriye dönük uyumluluk
#if swift(>=5.9)
@MainActor
struct ModernView: View {
var body: some View {
Text("Modern SwiftUI")
}
}
#else
struct ModernView: View {
var body: some View {
Text("Legacy SwiftUI")
}
}
#endif
canImport(ModuleName) fonksiyonu, belirtilen modülün mevcut derleme ortamında kullanılabilir olup olmadığını kontrol eder. Bu en esnek mekanizmadır: belirli bir platforma bağlı değildir. Örneğin, CoreHaptics kullanan kod yalnızca bu framework'ün mevcut olduğu cihazlarda derlenir.
#if canImport(CoreHaptics)
import CoreHaptics
class HapticManager {
private var engine: CHHapticEngine?
func playTapFeedback() {
guard let engine else { return }
// Dokunsal geri bildirim uygulaması
}
}
#endif
Kotlin dilinin ön işlemci direktifleri yoktur. Bunun yerine, Android ekosistemi üç alternatif sunar: BuildConfig alanları (çalışma zamanı kontrolleri), sourceSets (tüm dosyaları değiştirme) ve expect/actual (Kotlin Multiplatform'da).
Gradle sourceSets, farklı flavor'lar veya derleme türleri için farklı sınıf uygulamalarına sahip olmayı sağlar. src/debug/ dizininde hata ayıklama uygulaması, src/release/ dizininde ise sürüm uygulaması bulunur. Derleme sırasında Gradle uygun sourceSet'i seçer ve yalnızca onun dosyalarını derler.
// 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) {
// Sürümde No-op
}
}
KMP, expect mekanizmasını (ortak kodda bildirim) ve actual (belirli bir platform için uygulama) sağlar. Bu, derleme zamanı mekanizmasıdır: iOS için iOS sourceSet'inden actual uygulama derlenir; Android için Android sourceSet'inden. Hedef olmayan uygulamalar derlenmez.
// commonMain — expect bildirimi
expect fun getPlatformName(): String
// androidMain — Android için actual
actual fun getPlatformName(): String =
"Android \${Build.VERSION.SDK_INT}"
// iosMain — iOS için actual
actual fun getPlatformName(): String =
UIDevice.current.systemName() + " " + UIDevice.current.systemVersion
Android NDK aracılığıyla yerel kütüphaneler geliştirirken, #ifdef, #ifndef, #define direktifleriyle klasik C/C++ ön işlemcisi kullanılır. Swift'in aksine, C ön işlemcisi metin düzeyinde çalışır — aktif olmayan dallardaki kod sözdizimsel olarak yanlış olabilir.
NDK her platform için makrolar tanımlar: __ANDROID__ (Android), __APPLE__ (iOS/macOS), __linux__ (Linux). Mimariler için: __arm__, __aarch64__, __x86_64__. Bu makrolar, hedef platform için derleme yapılırken derleyici tarafından otomatik olarak ayarlanır.
// Android ve iOS için yerel kod
#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 ile çalışırken, C/C++ ön işlemcisinin bir metin değiştirme olduğunu hatırlamak önemlidir. Aktif olmayan bir daldaki sözdizimsel hatayı derleyici görmez, ancak yanlış bir #define aktif bir dalı bozarsa — hata ortaya çıkar. #define zincirlerini en aza indirmek ve constexpr sabitleri kullanmak önerilir.
UniFFI ve Mozilla Application Services aracılığıyla mobil geliştirmede de kullanılan Rust için kendi mekanizması vardır — Cargo.toml'daki özellik bayrakları. Rust'ta #[cfg(target_os = “android”)] gibi bayraklar Swift direktiflerine benzer şekilde çalışır: kontrol ön işlemci değil, derleyici düzeyinde yapılır. Bu, Rust'ı tek bir kod tabanından Android ve iOS için derlenmesi gereken yerel kütüphaneler için çekici bir seçim haline getirir.
Conditional Compilation kesin olarak tanımlanmış senaryolarda etkilidir. Yanlış kullanıldığında, test edilmesi ve bakımı zor kod kokusu (code smell) oluşturur. Doğru senaryoları ve tipik hataları inceleyelim.
İlk senaryo — platform soyutlaması: içinde Conditional Compilation'ın platform uygulamasını seçtiği tek bir cephe. İkincisi — hata ayıklama ve profilleme: sürüme dahil edilmemesi gereken geliştirici araçları. Üçüncüsü — geriye dönük uyumluluk: minimum sürüm güncellenene kadar eski işletim sistemi sürümlerinin desteklenmesi.
| Senaryo | Dil | Koşul |
|---|---|---|
| Platform soyutlaması | Swift | #if os(iOS) |
| Hata ayıklama | Swift/ObjC | #if DEBUG |
| Geriye dönük uyumluluk | Swift | #if swift(>=5.7) |
| Yerel kütüphane | C/C++ | #ifdef __ANDROID__ |
| A/B testi | Java/Kotlin | BuildConfig.FLAVOR |
En tehlikeli anti-örüntü — kod boyunca direktiflerin yayılması. Her iki dosyadan biri #if içeriyorsa, bu mimarinin yeniden düzenlenmesi gerektiğinin işaretidir. Doğru çözüm — platform kodunu protokollerin/arabirimlerin arkasına çıkarmak ve Bağımlılık Enjeksiyonu kullanmaktır.
Sıkça sorulan sorular
Conditional Compilation derleme zamanında çalışır: aktif olmayan kod ikili dosyaya girmez. Çalışma zamanı kontrolleri (if / switch) her zaman derlenir, koşul yürütme sırasında kontrol edilir. Birincisi daha güvenli ve verimlidir, ikincisi daha esnektir (yeniden derleme olmadan değiştirilebilir).
Evet, Swift fonksiyon içinde #if, döngüler içinde ve hatta ifadeler içinde direktiflere izin verir. Bu, Swift'in ilk sürümlerinde eksik olan özelliklerden biridir. Örneğin: let x = #if DEBUG 1 #else 0 #endif — geçerli kod.
Kotlin geliştiricileri, ön işlemciyi kırılgan kod kaynağı olarak görerek bilinçli bir şekilde reddettiler. Bunun yerine, expect/actual (derleme zamanı güvenliği) ve Gradle sourceSets (dosya düzeyinde izolasyon) sunarlar. Her iki yaklaşım da metin değiştirmeden daha güvenilirdir.
CI'da farklı bayrak kombinasyonlarıyla uygulamayı derleyin. Swift için: farklı Active Compilation Conditions ile ayrı Xcode şemaları yapılandırın. Android için: ayrı Build Variants yapılandırın ve her biri için testler çalıştırın. Otomasyon zorunludur.
Swift'te #if koşulu bir derleyici direktifidir. Koşulun kendisi sözdizimsel olarak yanlışsa (örneğin, os() adında yazım hatası), derleyici bir derleme hatası verecektir. C/C++'ta, ön işlemci makroyu bulamaz ve koşul yanlış olur.
Özet
Anahtar teslim bir mobil uygulama geliştireceğiz
IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.
Ayrıca okuyun