What does "#if DEBUG" really mean in Swift?

I've seen examples of Swift code with #if DEBUG / #endif, and have even used it myself.


I know exactly how preprocessor macros work in c, c++ and Objective-C. In those languages, I know I can say:


#ifdef DEBUG

... some code ...

#endif


And I know that if the DEBUG flag is false at build time, the code will not only not run, but it won't even be compiled. This is important to me because the code inside that block contains some sensitive information that must not end up in my compiled code for non-debug builds.


In Swift, I'm really not sure what happens. I know the code doesn't execute, but I'm also told that Swift doesn't have a preprocessor. So, what exactly is going on here?


Specifically, can I hide sensitive information inside an #if DEBUG block in Swift and be assured that it won't get compiled or in any way be present in the executable when the DEBUG flag is false? Or is #if DEBUG evaluated at runtime in Swift?


Thanks,

Frank

Replies

It is correct that Swift doesn't have a preprocessor.


But it is clearly documented the condition of #if/#endif is evaluated at compile time and the code block is conditionallly compiled.

(docs.swift.org/swift-book/ReferenceManual/Statements.html#ID538)


You can be sure that the line inside the #if DEBUG ... #endif is not even compiled and the generated code has no info about it when DEBUG is false.


But I doubt if it is a good idea to embed such a sensitive data even if it besides in DEBUG block. With a slight mistake, the condition of the #if might be true.

Great, thank you for the answer.


The statement "Swift doesn't have a preprocessor" is what confused me. Obviously, it does have a preprocessor. It may not do as much as the one in the C languages, but if it's causing code to be conditionally compiled, then it's a preprocessor.


And frankly, I have no clue what Apple or anyone else has against the C preprocessor. One of the most useful tools in the toolbox. I'm tired of compiler people taking away our best tools because they think we can't use them responsibly. End of rant.


Frank

Obviously, it does have a preprocessor.

No it does not. It has conditional compilation, but that’s not implemented via a preprocessor but rather implemented as part of the core language parser.

And frankly, I have no clue what Apple or anyone else has against the C preprocessor.

The preprocessor makes advanced tooling very hard. For example, it’s very hard to reliably syntax colour C-based languages because you’ve no idea what the C preprocessor might be doing to the text [1]. If you want your tools to progress, you have to leave some things behind.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

[1] As an aside, I once worked on a C project where I was getting really mysterious errors that I eventually tracked down to some clever soul doing the following:

#define void int

Preprocessor is very helpful when I want to test different programming methods. For example:


#define selMethod (1)


#if (selMethod==0)

// .....

#else if (selMethod==1)

// ....

#else if (selMethod==2)

// ....

#else

#error "Illegal selMethod definition."

#endif


I am new to Swift and Xcode. I cannot find any method to define my complication switch. It makes me crazy. 😟

The best way to handle this in Swift depends on the code that you’re conditionalising. If you’re just switching between implementations and all of those implementations actually compile, let the compiler ‘see’ all of them and write code to dispatch to the correct one:

private func method0() {
    …
}

private func method1() {
    …
}

private func method2() {
    …
}

func method() {
    switch 1 {
    case 0: self.method0()
    case 1: self.method1()
    case 2: self.method2()
    default: fatalError()
    }
}

This has the advantage that it ensures that all of the implementations are valid code at all times.

If the implementations won’t all compile together, you can do this:

#if SEL_METHOD_0

func method() {
}

#endif

#if SEL_METHOD_1

func method() {
}

#endif

#if SEL_METHOD_2

func method() {
}

#endif

and then define

SEL_METHOD_***
in the Active Compilation Conditions (
SWIFT_ACTIVE_COMPILATION_CONDITIONS
) build setting.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"
  • @eskimo im curious since im new to swift. is it possible to mix #if debug with another conditional? Like i need to have 2 conditionals and one of them is if it's debug.

Add a Comment

Even in C, regular if statements are often a better choice. In an if-else chain like the following, if selMethod is a constant value, then the compiler will omit the unused parts (thanks to standard constant-folding and dead-code-elimination optimizations). Plus you have the option of making selMethod be variable in the future:

   static const int selMethod = 4;

   if (selMethod == 0) {
       ... method 0 ...
   } else if (selMethod == 1) {
       ... method 1 ...
   } else if (selMethod == 2) {
       ... method 2 ...
   } else {
       abort("Invalid selMethod");
   }

And frankly, I have no clue what Apple or anyone else has against the C preprocessor. One of the most useful tools in the toolbox. I'm tired of compiler people taking away our best tools because they think we can't use them responsibly. End of rant.

Neither Apple nor anyone else has anything against the C preprocessor, in fact they're likely using it themselves. Swift is an entirely different language, not related to C or C++ and most other languages don't have a preprocessor either. Swift never had a preprocessor, no one took anything away. In fact, Apple has been adding more preprocessor-like constructs over the years.

That being said, I do wish Apple would add more ways to conditionally compile out code in Swift, particularly how you can do with macros in C/C++, primarily for confidentiality reasons so that things like debugging message strings and other debug data don't end up in the production executable.