Active Compilation Conditions คือแฟล็กที่ส่งให้คอมไพเลอร์ในขั้นตอนการbuild และอนุญาตให้รวมหรือแยกบล็อกโค้ดบางส่วนออกจากไฟล์ไบนารีสุดท้าย ตามเอกสารของ Apple Developer Documentation (2026) Swift รองรับ Active Compilation Conditions ผ่านคีย์ OTHER_SWIFT_FLAGS และคำสั่ง #if Active Compilation Conditions ช่วยให้นักพัฒนาสามารถสร้างโค้ดเวอร์ชันต่างๆ สำหรับการดีบัก การทดสอบ และการผลิตได้โดยไม่ต้องตรวจสอบขณะรันไทม์
ประเด็นสำคัญ
Active Compilation Conditions คือแฟล็กการคอมไพล์ที่กำหนดชุดคำสั่งพรีโปรเซสเซอร์ที่ทำงานอยู่ในขณะbuild ซึ่งแตกต่างจากแฟล็กขณะรันไทม์ (การตรวจสอบ if (isDebug)) เงื่อนไขการคอมไพล์จะแยกโค้ดที่ไม่ทำงานออกจากไฟล์ไบนารีอย่างแท้จริง ส่งผลให้ประสิทธิภาพดีขึ้นและขนาดแอปพลิเคชันลดลง
กลไกทำงานในระดับ พรีโปรเซสเซอร์หรือช่วงต้นของการคอมไพล์: คอมไพเลอร์ได้รับรายชื่อที่ทำงานอยู่ และเมื่อพบคำสั่ง #if NAME จะตรวจสอบว่า NAME อยู่ในรายการนั้นหรือไม่ หากไม่มีชื่อดังกล่าว โค้ดภายในบล็อกจะถูกข้ามและไม่ถูกคอมไพล์
ตามข้อมูลจาก Swift.org Blog (2025) การใช้ Active Compilation Conditions แทนแฟล็กขณะรันไทม์ช่วยลดขนาดไบนารีรีลีสได้เฉลี่ย 12-18% สำหรับโปรเจกต์ที่มีระบบการบันทึกและเครื่องมือดีบักที่ครอบคลุม ซึ่งมีความสำคัญอย่างยิ่งสำหรับแอปพลิเคชันมือถือที่มีข้อจำกัดด้านขนาดไฟล์ติดตั้ง
ความแตกต่างหลักจากการคอมไพล์แบบมีเงื่อนไขในระดับพรีโปรเซสเซอร์ของ C/C++ คือ Active Compilation Conditions ใน Swift และ Kotlin ทำงานในระดับ AST (Abstract Syntax Tree) ของคอมไพเลอร์ ไม่ใช่ในระดับการแทนที่ข้อความ ทำให้ปลอดภัยและคาดเดาได้มากขึ้น: ข้อผิดพลาดทางไวยากรณ์ในสาขา #if ที่ไม่ทำงานจะถูกตรวจพบในขั้นตอนการแยกวิเคราะห์ ไม่ใช่แสดงอาการขณะรันไทม์
ข้อแตกต่างที่สำคัญอีกประการคือ ใน Swift เงื่อนไข #if os(iOS) || os(macOS) จะถูกตรวจสอบ ณ เวลาคอมไพล์และทำงานกับชื่อแพลตฟอร์ม ไม่ใช่แมโครพรีโปรเซสเซอร์ ซึ่งช่วยขจัดข้อบกพร่องทั้งคลาสที่เกี่ยวข้องกับการแทรกข้อความที่ไม่ถูกต้องผ่าน #define ที่อาจเกิดขึ้นในพรีโปรเซสเซอร์ของ C/C++ คอมไพเลอร์ Swift มองเห็น AST ไม่ใช่ข้อความที่ถูกแทนที่ ทำให้การดีบักการคอมไพล์แบบมีเงื่อนไขง่ายขึ้นอย่างมาก
Swift รองรับ Active Compilation Conditions ผ่านคำสั่ง #if ซึ่งรับรายชื่อแฟล็กที่รวมกับตัวดำเนินการทางตรรกะ &&, || และ ! คอมไพเลอร์จะรวมโค้ดภายใน #if ... #endif เฉพาะเมื่อเงื่อนไขเป็นจริงเท่านั้น
Swift มีเงื่อนไขในตัวหลายอย่าง: DEBUG (ทำงานอัตโนมัติใน build แบบดีบัก), swift(>=5.0) (ตรวจสอบเวอร์ชันคอมไพเลอร์), canImport(UIKit) (ตรวจสอบความพร้อมใช้งานของโมดูล) และ targetEnvironment(simulator) (ตรวจสอบสภาพแวดล้อม) เงื่อนไขเหล่านี้ไม่ต้องการการกำหนดค่าเพิ่มเติม
// เงื่อนไขในตัวของ Swift
#if DEBUG
print("build แบบดีบัก — การบันทึกทำงาน")
#endif
#if canImport(UIKit)
import UIKit
let screen = UIScreen.main.bounds
#elseif canImport(AppKit)
import AppKit
let screen = NSScreen.main?.frame
#endif
นักพัฒนาสามารถเพิ่มแฟล็กของตนเองผ่านการตั้งค่า Build Setting OTHER_SWIFT_FLAGS ใน Xcode โดยระบุแฟล็กด้วยคำนำหน้า -D เช่น -DBETA หรือ -DANALYTICS_ENABLED สามารถตั้งค่าชุดแฟล็กที่แตกต่างกันสำหรับการกำหนดค่าต่างๆ (Debug, Release, Staging)
// การจัดการแฟล็ก BETA แบบกำหนดเอง
#if BETA
let apiEndpoint = "https://beta.api.com"
let isLoggingEnabled = true
#else
let apiEndpoint = "https://api.com"
let isLoggingEnabled = false
#endif
func trackEvent(_ name: String) {
#if ANALYTICS_ENABLED
Analytics.log(name)
#endif
}
Swift รองรับเงื่อนไขแพลตฟอร์ม: os(iOS), os(macOS), os(tvOS), os(watchOS), os(Linux), os(Windows) เงื่อนไขเหล่านี้ตรวจสอบแพลตฟอร์มเป้าหมายของการbuild และอนุญาตให้เขียนโค้ดที่ใช้ร่วมกันระหว่างแพลตฟอร์ม Apple หลายรายการพร้อมบล็อกเฉพาะแพลตฟอร์ม
import Foundation
func getDeviceName() -> String {
#if os(iOS)
return UIDevice.current.name
#elseif os(macOS)
return Host.current.name ?? "Unknown"
#else
return "Other platform"
#endif
}
ในระบบนิเวศ Android Active Compilation Conditions ถูกนำไปใช้ผ่านระบบ BuildConfig, productFlavors และแฟล็กใน build.gradle.kts Kotlin ไม่มีสิ่งที่เทียบเท่าโดยตรงกับคำสั่ง #if ในระดับภาษาแต่มีกลไกอื่นให้เลือก
วิธีที่พบมากที่สุดคือการเพิ่ม buildConfigField สำหรับแต่ละแฟล็ก: buildConfigField("boolean", "BETA", "true") ฟิลด์เหล่านี้จะถูกสร้างขึ้นในคลาส BuildConfig สำหรับแต่ละ Build Variant แยกกัน ฟิลด์ DEBUG มีอยู่ในตัวแล้วและเป็น true โดยอัตโนมัติสำหรับ build แบบดีบัก
// build.gradle.kts
android {
buildTypes {
debug {
buildConfigField("boolean", "BETA", "true")
}
release {
buildConfigField("boolean", "BETA", "false")
}
}
}
// การใช้งานในโค้ด Kotlin
if (BuildConfig.BETA) {
enableBetaFeatures()
}
Gradle อนุญาตให้สร้างไดเรกทอรี sourceSets แยกต่างหากสำหรับแต่ละรสชาติ ตัวอย่างเช่น src/demo/ และ src/full/ คลาสที่มีชื่อเดียวกันใน sourceSets ที่แตกต่างกันจะแทนที่กันเมื่อbuild รสชาติที่เกี่ยวข้อง ซึ่งเป็นกลไกที่ทรงพลังกว่าแฟล็กเพราะสามารถแทนที่คลาสทั้งคลาสได้
// src/demo/java/com/example/Config.kt
object Config {
const val API_URL = "http://demo.api.com"
const val IS_BETA = true
}
// src/full/java/com/example/Config.kt
object Config {
const val API_URL = "https://full.api.com"
const val IS_BETA = false
}
สำหรับ Kotlin Multiplatform (KMP) มีคำสั่ง expect/actual ซึ่งอนุญาตให้ประกาศการประกาศที่คาดหวังในโค้ดทั่วไปและให้การใช้งานเฉพาะแพลตฟอร์ม นี่เป็นกลไกระดับคอมไพเลอร์ที่คล้ายกับ Active Compilation Conditions ในแง่ของผลลัพธ์ — โค้ดที่ไม่ทำงานจะไม่ถูกคอมไพล์สำหรับแพลตฟอร์มที่ไม่เหมาะสม
Active Compilation Conditions ถูกใช้ในสี่สถานการณ์หลัก: การดีบัก (บันทึก, ตัวตรวจสอบ), การทดสอบ A/B (แฟล็กฟีเจอร์), การปรับตัวตามแพลตฟอร์ม (โค้ดที่ใช้ร่วมกัน iOS/macOS) และ การอนุญาตสิทธิ์ (เวอร์ชันฟรี/เสียเงิน)
สถานการณ์ที่พบบ่อยที่สุดคือการบันทึกแบบมีเงื่อนไข ในbuild แบบดีบัก บันทึกทั้งหมดจะถูกเขียนไปยังคอนโซล; ในbuild แบบรีลีส จะไม่มีการบันทึกอะไรเลย การใช้ #if DEBUG หรือ BuildConfig.DEBUG ช่วยให้มั่นใจได้ว่าไบนารีรีลีสจะไม่มีการเรียก logger แม้แต่ครั้งเดียว รวมถึงที่ถูกอินไลน์
func logRequest(_ url: URL, _ statusCode: Int) {
#if DEBUG
Logger.debug("Request to \(url.absoluteString) returned \(statusCode)")
#endif
}
หากฟีเจอร์ใหม่ยังไม่พร้อมสำหรับการผลิต แต่มีอยู่ในโค้ดแล้ว สามารถซ่อนไว้เบื้องหลังแฟล็กการคอมไพล์ได้ แตกต่างจากแฟล็กฟีเจอร์ขณะรันไทม์ แฟล็กการคอมไพล์ไม่สร้างภาระให้แอปพลิเคชันด้วยการตรวจสอบและไม่สามารถเปิดใช้งานโดยผู้ใช้
ควรใช้ Active Compilation Conditions อย่างพอประมาณ จำนวนแฟล็กที่มากเกินไปทำให้โค้ดเข้าใจยาก: นักพัฒนาไม่สามารถแน่ใจได้ว่าสาขาใดจะถูกคอมไพล์ในขณะนั้น ขอแนะนำให้บันทึกแต่ละแฟล็กใน README หรือไฟล์ CONFIG.md โดยเฉพาะ
ในโปรเจกต์ขนาดใหญ่ที่มีทีมกระจายอยู่ทั่วไป การนำ การตรวจสอบแฟล็กอัตโนมัติ ใน CI มาใช้มีประโยชน์มาก ทุกคำขอ pull request ควรผ่านการbuild ด้วยชุดค่าผสมที่เป็นไปได้ทั้งหมดของ Active Compilation Conditions ซึ่งช่วยให้มั่นใจว่าโค้ดภายใต้แฟล็กที่ไม่ทำงานไม่เสียหายจากการปรับโครงสร้าง และไม่มีสาขาการคอมไพล์แบบมีเงื่อนไขใดที่ไม่ถูกทดสอบจนกว่าจะถึงเวลารีลีส เครื่องมือเช่น xcresulttool (สำหรับ iOS) และ Gradle Build Scan (สำหรับ Android) ช่วยทำให้กระบวนการนี้เป็นอัตโนมัติ
คำถามที่พบบ่อย
#if DEBUG คือคำสั่งการคอมไพล์: หาก DEBUG ไม่ทำงาน โค้ดภายในบล็อกจะไม่เข้าไปในไบนารี if (isDebug) คือการตรวจสอบขณะรันไทม์: โค้ดจะถูกคอมไพล์เสมอ เงื่อนไขจะถูกตรวจสอบระหว่างการทำงาน #if ไม่ทิ้งร่องรอยไว้ในbuild แบบรีลีส
ในการตั้งค่า Build Settings ของโปรเจกต์ ค้นหา Other Swift Flags (OTHER_SWIFT_FLAGS) และเพิ่มบรรทัดใหม่ด้วยแฟล็ก: -DMY_FLAG แฟล็กจะปรากฏให้คำสั่ง #if MY_FLAG เห็น สามารถตั้งค่าแฟล็กที่แตกต่างกันสำหรับการกำหนดค่า Debug และ Release
Kotlin/JVM ไม่มีสิ่งที่เทียบเท่าโดยตรง แต่จะใช้ ฟิลด์ BuildConfig แทน (การตรวจสอบขณะรันไทม์ แต่ ProGuard อาจลบโค้ดที่ไม่ใช้) ใน Kotlin Multiplatform — คำสั่ง expect/actual ในระดับการประกาศ
ได้ Swift รองรับตัวดำเนินการทางตรรกะ: #if DEBUG && BETA, #if os(iOS) || os(tvOS), #if !RELEASE สามารถจัดกลุ่มเงื่อนไขด้วยวงเล็บสำหรับตรรกะที่ซับซ้อน AND และ OR ทำงานตามกฎ short-circuit มาตรฐาน
การแสดงตัวอย่าง SwiftUI จะถูกbuild ในกระบวนการแยกต่างหากด้วยแฟล็กที่แตกต่างจากเป้าหมายหลัก DEBUG อาจไม่ทำงาน วิธีแก้ไข: ใช้ targetEnvironment(simulator) สำหรับโค้ดแสดงตัวอย่าง หรือแยกตรรกะแบบมีเงื่อนไขออกเป็นเมธอดแยกต่างหาก
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม