Preprocessor directives such as #if, #else, #endif, and #define are powerful tools in Objective-C and Swift. They enable developers to conditionally compile code, manage different build configurations, and optimize apps for various platforms or environments. Understanding these concepts helps streamline code, improve debugging, and create more flexible, maintainable projects.
In the following post, I will present code snippets that demonstrate the use of preprocessing directives. I hope you find them useful!

Preprocessing directives

Preprocessing directives in Swift are instructions that are interpreted by the Swift compiler before the actual compilation of the code begins. These directives allow developers to include or exclude portions of code based on certain conditions, such as the target operating system, compiler flags, or custom-defined conditions. Common preprocessing directives in Swift include #if#elseif#else, and #endif, which are used for conditional compilation. For example, you can use these directives to write platform-specific code, ensuring that only the relevant code for a particular platform (like iOS or macOS) is compiled. This helps in maintaining a single codebase for multiple platforms while still accommodating platform-specific requirements.

The concept of preprocessing directives originates from the C programming language, where the C preprocessor (cpp) is used to manipulate the source code before it is compiled. Swift, being influenced by C and Objective-C, adopted a similar mechanism but with some differences. Unlike C, Swift does not have a separate preprocessor; instead, these directives are handled directly by the Swift compiler. This integration simplifies the build process and avoids some of the complexities and pitfalls associated with traditional preprocessors. The use of preprocessing directives in Swift reflects the language’s goal of providing modern, safe, and efficient tools for developers while maintaining compatibility with existing practices from its predecessor languages.

#if, #elsif, #else and #endif

Platform-Specific Code

You can use #if to write code that compiles only for specific platforms, such as iOS, macOS, or Linux. This is useful for handling platform-specific APIs or behavior.

#if os(iOS)
    print("Running on iOS")
#elseif os(macOS)
    print("Running on macOS")
#elseif os(Linux)
    print("Running on Linux")
#else
    print("Running on an unknown platform")
#endif

Custom Compiler Flags

Also you can define custom compiler flags in your build settings and use them to conditionally compile code. For example, you might want to include debug-only code or feature toggles.

#if DEBUG
    print("Debug mode is enabled")
#elseif RELEASE
    print("Release mode is enabled")
#else
    print("Unknown build configuration")
#endif

Feature Toggles

Use conditional compilation to enable or disable features based on custom conditions.

        #if EXPERIMENTAL_FEATURE
            print("Experimental feature is enabled")
        #else
            print("Experimental feature is disabled")
        #endif
 

Open the target settings and add -DEXPERIMENTAL_FEATURE on Other Swift Flags

Checking Swift Version

You can use conditional compilation to check the Swift version being used.

#if swift(>=5.0)
    print("Using Swift 5.0 or later")
#else
    print("Using an older version of Swift")
#endif
 

#available

The #available directive in Swift is used to check the availability of APIs at runtime based on the operating system version or platform. This is particularly useful when you want to use newer APIs while maintaining compatibility with older versions of the operating system.

Purpose of #available is ensure that your app does not crash when running on older versions of the operating system that do not support certain APIs. Also it allows you to provide fallback behavior for older OS versions.

if #available(iOS 15, macOS 12.0, *) {
    // Use APIs available on iOS 15 and macOS 12.0 or later
    let sharedFeature = SharedFeatureClass()
    sharedFeature.doSomething()
} else {
    // Fallback for older versions
    print("This feature requires iOS 15 or macOS 12.0.")
}

If your code runs on multiple platforms (e.g., iOS and macOS), you can check availability for each platform.

#warning and #error:

The #warning and #error directives in Swift are used to emit custom warnings or errors during compilation. These are helpful for:

  1. Marking incomplete or problematic code.

  2. Enforcing coding standards or requirements.

  3. Providing reminders for future work.

  4. Preventing compilation if certain conditions are not met.

Unlike runtime warnings or errors, these directives are evaluated at compile time, meaning they can catch issues before the code is even run.

The #warning directive generates a compile-time warning. It does not stop the compilation process but alerts the developer to potential issues or tasks that need attention.

func fetchData() {
    #warning("Replace with network call once API is ready")
    let data = mockData()
}

The #error directive generates a compile-time error. It stops the compilation process entirely, ensuring that the code cannot be built until the issue is resolved.

func newFeature() {
    #error("This feature is not yet implemented.")
}

Conclusions

In this post, I have provide you a few preprocessiong directives that I have considered most usefull. You can find source code used for writing this post in following repository

Copyright © 2024-2025 JaviOS. All rights reserved