Section 2/101 menit

2. Directive Conditional Compilation

2. Directive Conditional Compilation

2.1 #if swift(>=X.Y) — Berdasarkan Language Version

Paling umum. Bergantung pada nilai SWIFT_VERSION di Build Settings, bukan versi Xcode:

swift
// Guard syntax dan fitur language baru
#if swift(>=6.0)
// Kode ini hanya dikompilasi saat SWIFT_VERSION = 6
func parse(_ data: Data) throws(ParseError) -> User {
    // typed throws — hanya ada di Swift 6
}
#else
// Kode ini dikompilasi saat SWIFT_VERSION = 5
func parse(_ data: Data) throws -> User {
    // untyped throws — kompatibel Swift 5
}
#endif
swift
// Sendable conformance yang di-enforce
#if swift(>=6.0)
struct AppConfig: Sendable {
    let apiURL: URL
    let timeout: TimeInterval
}
#else
struct AppConfig {
    let apiURL: URL
    let timeout: TimeInterval
}
#endif

2.2 #if compiler(>=X.Y) — Berdasarkan Versi Toolchain

Bergantung pada versi compiler (Xcode), bukan language version. Berguna untuk API compiler yang baru tapi tidak terkait language version:

swift
// Compiler version check — independen dari SWIFT_VERSION
#if compiler(>=6.0)
// Tersedia saat menggunakan Xcode 16+ / Swift 6 toolchain
// Meski SWIFT_VERSION masih = 5
import Observation  // @Observable macro tersedia
#else
import Combine      // fallback ke Combine untuk toolchain lama
#endif

Perbedaan kritis:

swift
// Skenario: Xcode 16, SWIFT_VERSION = 5

#if swift(>=6.0)
    // TIDAK dikompilasi — language version masih 5
#endif

#if compiler(>=6.0)
    // DIKOMPILASI — compiler adalah Xcode 16 (Swift 6 toolchain)
#endif

2.3 #if canImport() — Berdasarkan Ketersediaan Module

Paling robust untuk library/module baru. Tidak peduli versi — jika module ada, gunakan:

swift
// Module Synchronization hanya ada di Swift 6 / macOS 15 / iOS 18
#if canImport(Synchronization)
import Synchronization

struct ThreadSafeCounter {
    private let value = Mutex(0)
    
    func increment() {
        value.withLock { $0 += 1 }
    }
    
    func current() -> Int {
        value.withLock { $0 }
    }
}

#else
import Foundation

final class ThreadSafeCounter {
    private let lock = NSLock()
    private var _value = 0
    
    func increment() {
        lock.withLock { _value += 1 }
    }
    
    func current() -> Int {
        lock.withLock { _value }
    }
}
#endif
swift
// Observation framework — @Observable macro
#if canImport(Observation)
import Observation

@Observable
class UserViewModel {
    var name: String = ""
    var isLoading: Bool = false
}

#else
import Combine

class UserViewModel: ObservableObject {
    @Published var name: String = ""
    @Published var isLoading: Bool = false
}
#endif

2.4 #if hasFeature() — Berdasarkan Fitur Language Spesifik

Cara paling granular — guard satu fitur tanpa terikat versi angka. Berguna saat fitur di-backport atau di-enable secara selektif:

swift
// Guard fitur noncopyable types
#if hasFeature(NoncopyableGenerics)
struct FileDescriptor: ~Copyable {
    private let fd: Int32
    
    init?(path: String) {
        let descriptor = Darwin.open(path, O_RDONLY)
        guard descriptor != -1 else { return nil }
        self.fd = descriptor
    }
    
    deinit { Darwin.close(fd) }
    
    borrowing func read(count: Int) -> Data? {
        var buffer = [UInt8](repeating: 0, count: count)
        let n = Darwin.read(fd, &buffer, count)
        return n > 0 ? Data(buffer.prefix(n)) : nil
    }
}
#else
// Fallback: class dengan manual lifecycle
final class FileDescriptor {
    private let fd: Int32
    
    init?(path: String) {
        let descriptor = Darwin.open(path, O_RDONLY)
        guard descriptor != -1 else { return nil }
        self.fd = descriptor
    }
    
    deinit { Darwin.close(fd) }
    
    func read(count: Int) -> Data? {
        var buffer = [UInt8](repeating: 0, count: count)
        let n = Darwin.read(fd, &buffer, count)
        return n > 0 ? Data(buffer.prefix(n)) : nil
    }
}
#endif
swift
// Guard typed throws
#if hasFeature(TypedThrows)
func validateAge(_ value: Int) throws(ValidationError) -> Int {
    guard value >= 0 else { throw ValidationError.negative }
    guard value <= 150 else { throw ValidationError.tooLarge }
    return value
}
#else
func validateAge(_ value: Int) throws -> Int {
    guard value >= 0 else { throw ValidationError.negative }
    guard value <= 150 else { throw ValidationError.tooLarge }
    return value
}
#endif

2.5 @available — Untuk OS Version (Bukan Language Version)

Berbeda dari #if — ini adalah runtime check dan hanya untuk API ketersediaan OS:

swift
// Bukan untuk kondisi compile — ini runtime
if #available(iOS 18.0, macOS 15.0, *) {
    // Gunakan API baru
    let _ = Mutex(0)
} else {
    // Fallback
    let _ = NSLock()
}

// Untuk deklarasi tipe: @available sebagai annotation
@available(iOS 18.0, macOS 15.0, *)
struct NewAPIWrapper {
    // Hanya bisa dipakai di iOS 18+ / macOS 15+
}