شرایط کامپایل فعال در توسعه برنامه‌ها: مفاهیم کلیدی و کاربرد

نویسنده: IT Sectr منتشر شده: 2026-06-01 زمان مطالعه: 8 دقیقه

Active Compilation Conditions پرچم‌هایی هستند که در مرحله ساخت به کامپایلر منتقل می‌شوند و امکان شامل یا حذف بلوک‌های کد خاص از فایل باینری نهایی را فراهم می‌کنند. طبق Apple Developer Documentation (2026)، Swift از Active Compilation Conditions از طریق کلید OTHER_SWIFT_FLAGS و دستور #if پشتیبانی می‌کند. Active Compilation Conditions به توسعه‌دهندگان امکان می‌دهد نسخه‌های مختلف کد را برای اشکال‌زدایی، تست و تولید بدون بررسی‌های زمان اجرا بسازند.

نکات اصلی

  • Active Compilation Conditions — پرچم‌های کامپایل سفارشی که تعیین می‌کنند کدام بلوک‌های کد در باینری نهایی کامپایل شوند.
  • Swift از کلید OTHER_SWIFT_FLAGS در Xcode Build Settings برای تنظیم پرچم‌ها با پیشوند -D استفاده می‌کند.
  • دستور #if وجود پرچم را بررسی می‌کند: کد داخل #if DEBUG فقط در نسخه debug کامپایل می‌شود.
  • Android — قابلیت مشابه: فیلدهای BuildConfig و productFlavors در Gradle.
  • عملکرد — کامپایل شرطی برخلاف پرچم‌های زمان اجرا هیچ اثری در باینری release باقی نمی‌گذارد.

Active Compilation Conditions چیست

Active Compilation Conditions پرچم‌های کامپایل هستند که مجموعه دستورات پیش‌پردازنده فعال را در مرحله ساخت تعیین می‌کنند. برخلاف پرچم‌های زمان اجرا (بررسی if (isDebug))، شرایط کامپایل کد غیرفعال را از فایل باینری به صورت فیزیکی حذف می‌کنند که باعث افزایش عملکرد و کاهش اندازه برنامه می‌شود.

مکانیزم در سطح پیش‌پردازنده یا مراحل اولیه کامپایل کار می‌کند: کامپایلر لیستی از نام‌های فعال دریافت می‌کند و هنگام مواجهه با دستور #if NAME بررسی می‌کند که آیا NAME در این لیست وجود دارد یا خیر. اگر نام وجود نداشته باشد — کد داخل بلوک نادیده گرفته شده و کامپایل نمی‌شود.

طبق Swift.org Blog (2025)، استفاده از Active Compilation Conditions به جای پرچم‌های زمان اجرا اندازه باینری release را در پروژه‌های با سیستم لاگ‌گیری و ابزارهای اشکال‌زدایی پیشرفته به طور متوسط 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 را می‌بیند که اشکال‌زدایی کامپایل شرطی را بسیار آسان‌تر می‌کند.

Active Compilation Conditions در Swift

Swift از Active Compilation Conditions از طریق دستور #if پشتیبانی می‌کند که لیستی از نام پرچم‌ها را با عملگرهای منطقی &&، || و ! دریافت می‌کند. کامپایلر کد داخل #if ... #endif را فقط در صورت درست بودن شرط شامل می‌کند.

شرط‌های داخلی Swift

Swift چندین شرط داخلی ارائه می‌دهد: DEBUG (به طور خودکار در نسخه debug فعال است)، swift(>=5.0) (بررسی نسخه کامپایلر)، canImport(UIKit) (بررسی در دسترس بودن ماژول) و targetEnvironment(simulator) (بررسی محیط). این شرط‌ها نیاز به پیکربندی اضافی ندارند.

swift
// شرط‌های داخلی Swift
#if DEBUG
    print("نسخه اشکال‌زدایی — لاگ‌گیری فعال است")
#endif

#if canImport(UIKit)
    import UIKit
    let screen = UIScreen.main.bounds
#elseif canImport(AppKit)
    import AppKit
    let screen = NSScreen.main?.frame
#endif

پرچم‌های سفارشی در Xcode

توسعه‌دهنده می‌تواند پرچم‌های خود را از طریق Build Setting OTHER_SWIFT_FLAGS در Xcode اضافه کند. پرچم با پیشوند -D مشخص می‌شود، مثلاً -DBETA یا -DANALYTICS_ENABLED. برای پیکربندی‌های مختلف (Debug، Release، Staging) می‌توان مجموعه پرچم‌های متفاوتی تنظیم کرد.

swift
// پردازش پرچم سفارشی 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
}

شرط‌های پلتفرمی #if os()

Swift از شرط‌های پلتفرمی پشتیبانی می‌کند: os(iOS)، os(macOS)، os(tvOS)، os(watchOS)، os(Linux)، os(Windows). این شرط‌ها پلتفرم هدف ساخت را بررسی می‌کنند و امکان نوشتن کد مشترک برای چندین پلتفرم Apple با بلوک‌های خاص هر پلتفرم را فراهم می‌کنند.

swift
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 و Kotlin

در اکوسیستم Android، Active Compilation Conditions از طریق سیستم BuildConfig، productFlavors و پرچم‌ها در build.gradle.kts پیاده‌سازی می‌شوند. Kotlin معادل مستقیم دستور #if در سطح زبان ندارد، اما مکانیزم‌های جایگزین ارائه می‌دهد.

فیلدهای BuildConfig به عنوان پرچم

رایج‌ترین روش — اضافه کردن buildConfigField برای هر پرچم: buildConfigField("boolean", "BETA", "true"). این فیلدها در کلاس BuildConfig برای هر Build Variant جداگانه تولید می‌شوند. فیلد DEBUG از قبل داخلی است و برای نسخه‌های debug به طور خودکار true است.

kotlin
// build.gradle.kts
android {
    buildTypes {
        debug {
            buildConfigField("boolean", "BETA", "true")
        }
        release {
            buildConfigField("boolean", "BETA", "false")
        }
    }
}

// استفاده در کد Kotlin
if (BuildConfig.BETA) {
    enableBetaFeatures()
}

Source Sets و productFlavors

Gradle اجازه ایجاد دایرکتوری‌های جداگانه sourceSets برای هر flavor را می‌دهد. مثلاً src/demo/ و src/full/. کلاس‌های با نام یکسان در sourceSets مختلف هنگام ساخت flavor مربوطه جایگزین یکدیگر می‌شوند. این مکانیزم قدرتمندتری نسبت به پرچم‌ها است زیرا می‌توان کل کلاس‌ها را بازنویسی کرد.

kotlin
// 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) و مجوزدهی (نسخه‌های رایگان/پولی).

لاگ‌گیری اشکال‌زدایی

رایج‌ترین سناریو — لاگ‌گیری شرطی. در نسخه debug همه لاگ‌ها در کنسول نوشته می‌شوند، در release — هیچکدام. استفاده از #if DEBUG یا BuildConfig.DEBUG تضمین می‌کند که باینری release هیچ فراخوانی لاگر، حتی inline شده، نداشته باشد.

swift
func logRequest(_ url: URL, _ statusCode: Int) {
    #if DEBUG
        Logger.debug("Request to \(url.absoluteString) returned \(statusCode)")
    #endif
}

پرچم‌های ویژگی در مرحله ساخت

اگر قابلیت جدید هنوز برای تولید آماده نیست اما در کد وجود دارد، می‌توان آن را پشت یک پرچم کامپایل مخفی کرد. برخلاف پرچم‌های ویژگی زمان اجرا، پرچم‌های کامپایل برنامه را با بررسی‌ها سنگین نمی‌کنند و توسط کاربر قابل فعال‌سازی نیستند.

  • ویژگی‌های جدید — مخفی کردن قابلیت ناتمام تا انتشار بعدی بدون حذف کد
  • تحلیل — فعال کردن جمع‌آوری متریک اضافی فقط برای تست‌کنندگان بتا
  • SDKهای شخص ثالث — حذف کتابخانه‌های سنگین از نسخه رایگان برنامه
  • کامپوننت‌های UI — نمایش صفحات آزمایشی فقط در نسخه staging

بهترین روش‌ها

Active Compilation Conditions باید به میزان متعادل استفاده شوند. تعداد بیش از حد پرچم‌ها درک کد را دشوار می‌کند: توسعه‌دهنده نمی‌تواند مطمئن باشد کدام شاخه‌ها در حال حاضر کامپایل می‌شوند. توصیه می‌شود هر پرچم را در README یا فایل مخصوص CONFIG.md مستند کنید.

در پروژه‌های بزرگ با تیم توزیع شده، پیاده‌سازی اعتبارسنجی خودکار پرچم‌ها در CI مفید است. هر pull request باید با تمام ترکیبات ممکن Active Compilation Conditions ساخته شود. این تضمین می‌کند که کد زیر پرچم غیرفعال به دلیل بازسازی شکسته نشده و هیچ شاخه کامپایل شرطی تا زمان انتشار بررسی‌نشده باقی نمانده است. ابزارهایی مانند xcresulttool (برای iOS) و Gradle Build Scan (برای Android) به خودکارسازی این فرآیند کمک می‌کنند.

  • حداقل پرچم‌ها — بیش از 5-7 شرط فعال در هر پروژه. هر پرچم یک نقطه پیچیدگی است.
  • قرارداد نام‌گذاری — همه پرچم‌ها با UPPER_CASE، با پیشوند پروژه: MYAPP_BETA، MYAPP_ANALYTICS.
  • Code review — هر اضافه کردن #if یا buildConfigField باید بررسی جداگانه شود.
  • تست — CI باید حداقل یک بار در روز همه ترکیبات ممکن پرچم‌ها را بسازد.

سوالات متداول

تفاوت بین #if DEBUG و if (isDebug) در Swift چیست؟

#if DEBUG — یک دستور کامپایل است: اگر DEBUG فعال نباشد، کد داخل بلوک وارد باینری نمی‌شود. if (isDebug) — بررسی زمان اجرا: کد همیشه کامپایل می‌شود، شرط در حین اجرا بررسی می‌شود. #if هیچ اثری در نسخه release باقی نمی‌گذارد.

چگونه پرچم خود را در Xcode اضافه کنم؟

در Build Settings پروژه Other Swift Flags (OTHER_SWIFT_FLAGS) را پیدا کنید و یک خط جدید با پرچم اضافه کنید: -DMY_FLAG. پرچم برای دستور #if MY_FLAG قابل مشاهده خواهد بود. می‌توانید پرچم‌های متفاوتی برای پیکربندی‌های Debug و Release تنظیم کنید.

آیا در Kotlin معادلی برای Swift #if وجود دارد؟

در Kotlin/JVM معادل مستقیم وجود ندارد. به جای آن از فیلدهای BuildConfig (بررسی در زمان اجرا، اما ProGuard می‌تواند کد استفاده نشده را حذف کند) استفاده می‌شود. در Kotlin Multiplatform — دستور expect/actual در سطح اعلان‌ها.

آیا می‌توان چند پرچم را در یک #if ترکیب کرد؟

بله، Swift از عملگرهای منطقی پشتیبانی می‌کند: #if DEBUG && BETA، #if os(iOS) || os(tvOS)، #if !RELEASE. شرایط را می‌توان برای منطق پیچیده با پرانتز گروه‌بندی کرد. AND و OR طبق قوانین استاندارد اتصال کوتاه کار می‌کنند.

چرا #if DEBUG در پیش‌نمایش SwiftUI کار نمی‌کند؟

پیش‌نمایش SwiftUI در یک فرآیند جداگانه با پرچم‌های متفاوت از target اصلی ساخته می‌شود. DEBUG ممکن است فعال نباشد. راه حل: استفاده از targetEnvironment(simulator) برای کد پیش‌نمایش یا انتقال منطق شرطی به متدهای جداگانه.

خلاصه

  • Active Compilation Conditions — پرچم‌های کامپایل که کد غیرفعال را بدون بررسی‌های زمان اجرا از فایل باینری حذف می‌کنند.
  • Swift از #if با شرط‌های داخلی (DEBUG، os، canImport) و پرچم‌های سفارشی از طریق OTHER_SWIFT_FLAGS پشتیبانی می‌کند.
  • Android و Kotlin از فیلدهای BuildConfig، productFlavors و مکانیزم expect/actual در KMP استفاده می‌کنند.
  • عملکرد — کامپایل شرطی اندازه باینری را 12-18% در پروژه‌های با سیستم لاگ‌گیری پیشرفته کاهش می‌دهد.
  • پرچم‌های ویژگی در مرحله کامپایل برنامه را با بررسی‌ها سنگین نمی‌کنند و توسط کاربر قابل فعال‌سازی نیستند.
  • توصیه‌ها — بیش از 5-7 پرچم در هر پروژه، قرارداد نام‌گذاری با پیشوند، تست اجباری همه ترکیبات در CI.

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید