183

Before swift I would define a set of schemes for alpha, beta, and distribution builds. Each of these schemes would have a set of macros that were defined to gate certain behaviors at the project level. The simplest example is the DEBUG=1 macro that is defined by default for all Xcode projects in the default scheme for the Run build. One could query #ifdef DEBUG ... and make decisions in the code accordingly, even compiling out non-necessary code.

It seems that this type of configurational gating is not as easy using swift, as macros are not supported. Can someone suggest a comparable approach, I don't care if the code is compiled out, per se. I would like to gate features based on build scheme, though.

Sahil Kapoor
  • 11,183
  • 13
  • 64
  • 87
phoganuci
  • 4,984
  • 9
  • 39
  • 50

7 Answers7

487

In Swift you can still use the "#if/#else/#endif" preprocessor macros (although more constrained), as per Apple docs. Here's an example:

#if DEBUG
    let a = 2
#else
    let a = 3
#endif

Now, you must set the "DEBUG" symbol elsewhere, though. Set it in the "Swift Compiler - Custom Flags" section, "Other Swift Flags" line. You add the DEBUG symbol with the -D DEBUG entry.

(Build Settings -> Swift Compiler - Custom Flags) enter image description here

As usual, you can set a different value when in Debug or when in Release.

I tested it in real code; it doesn't seem to be recognized in a playground.

ohyes
  • 3,210
  • 3
  • 20
  • 24
Jean Le Moignan
  • 22,158
  • 3
  • 31
  • 37
  • 5
    Note that you can also use #elseif lines to add more tests. Interestingly enough, you can access the definition but extract nothing from it; that is, define -DDEBUG=5 (or ="FOO"), and then try to print it with "println(DEBUG is \(DEBUG\)". That line generates no errors, but does not do anything. – David H Jun 11 '14 at 14:02
  • Note that, as @Jean states, a key aspect of this is to add the setting to "Swift Compiler - Custom Flags". I had them set (a holdover from the original Obj-C project) in LLVM Preprocessing section. Interestingly the LLVM section *does* add the entries to the swift command line, but as '-DFlag', but when you add '-DFlag' to the 'Other Swift Flags' section it miraculously changes the flag to '-D Flag' on the swift command line. This latter behavior seems to be the key. So much for consistency... – David Hunt Jan 06 '15 at 19:15
  • How can I use custom environment variables in testing for example? – Daniel Gomez Rico Mar 06 '15 at 14:44
  • 10
    Note: "Built Settings -> Swift Compiler -> Custom Flags" not visible in "Basic" build settings. Must show "All" build settings for it to appear. – levibostian Sep 30 '15 at 21:30
  • 7
    @EugeneDubinin probably because you should make sure that `$(inherited)` is used in target settings to inherit project settings. – DanSkeel Dec 23 '15 at 11:57
  • 2
    @DanSkeel nice catch, adding `$(inherited)` makes my comment irrelevant, thank you! – Yevhen Dubinin Dec 23 '15 at 12:03
  • 2
    Small but important side note: They have to be in CAPITALS! – lukas_o Jun 03 '16 at 12:43
  • 14
    In Xcode 8 there is now also an "Active Compilation Conditions" setting in the "Swift Compiler - Custom Flags" section. You can add flags here without needing the -D – Marcus Oct 12 '16 at 15:27
  • Don't make the mistake of adding it wrong. Add “-DDEBUG” to the Debug section. Not "-D DEBUG" will not fix the issue. – Nick N Nov 30 '16 at 01:00
  • 2
    How did you manage to get the selected row on the Build Settings in green? – Juan Fran Jimenez Jun 12 '17 at 12:37
  • can I change this macro programatically at run time? I want to enable a button that switches to production APIs. On that button, I want to change DEBUG to 0 and display the message that user needs to restart the app. So next time it will use production APIs. – Hiren Prajapati Jan 03 '18 at 06:17
  • 2
    @JuanFranJimenez The green colour is actually a result of the global macOS highlight colour. Have a look in System Preferences -> General – wardw Feb 23 '18 at 18:42
  • 1
    @JuanFranJimenez to change the color to green go to Mac Icon > System Preferences > General > change Accent Color – Lance Samaria Jan 23 '23 at 14:20
34

We ran into an issue with not wanting to set swift compiler flags because we didn't want to have to set them and keep them up to date for different targets etc. Also, in our mixed codebase, we didn't want to make remember to set our flags appropriately all the time for each language.

For ours, we declared a file in ObjC

PreProcessorMacros.h

extern BOOL const DEBUG_BUILD;

In the .m

PreProcessorMacros.m

#ifdef DEBUG
    BOOL const DEBUG_BUILD = YES;
#else
    BOOL const DEBUG_BUILD = NO;
#endif

Then, in your Objective-C Bridging Header

#import "PreProcessorMacros.h"

Now, use this in your Swift codebase

if DEBUG_BUILD {
    println("debug")
} else {
    println("release")
}

This is definitely a workaround, but it solved our problem so I posted it here in the hopes that it will help. It is not meant to suggest that the existing answers are invalid.

Logan
  • 52,262
  • 20
  • 99
  • 128
  • 13
    The whole point of macro's is to change the code based on the build configuration. You are bringing the if back to the runtime, you don't need macro's for that. – Berik Jul 15 '15 at 09:51
  • 18
    @Berik - I posted a valid solution in the hopes that it might also help others trying to solve an aspect of this problem, particularly in multi-language projects. If your problem requires not compiling specific code that's fine. Also a comment is fine, particularly when it educates some why this might not be the solution for them. Also asking to make a note in the answer about the limitations of this approach. Downvoting is unnecessary and discourages alternative solutions that might be helpful to others solving similar problems. Also, op says "I don't care if the code is compiled out". – Logan Jul 15 '15 at 15:19
6

More swifty solution to Logans method. Set -D DEBUG in Other Swift Flags of Swift Compiler - Custom Flags section in build settings of your target.

Then declare following method in global scope:

#if DEBUG
let isDebugMode = true
#else
let isDebugMode = false
#endif

Now use it as

if isDebugMode {
    // Do debug stuff
}
Sahil Kapoor
  • 11,183
  • 13
  • 64
  • 87
4

For me, set the debug item of "Active Compilation Condition" to "DEBUG" worked.

Then using DEBGU key work in #IF DEBUG works in debug mode and #ELSE in release mode:

  1. Select your target,
  2. In Build Setting tab search for "Active Compilation Condition",
  3. Set the value of its "Debug" item to "YourKeyWord",
  4. Use simply as follow:

    #if DEBUG
        print("You'r running in DEBUG mode!")
    #else
        print("You'r running in RELEASE mode!")
    #endif
    
Marjan Basiri
  • 184
  • 1
  • 10
0

Swift compiler directives

You can use next compiler directive

#if <some_key>
    //logic 1
#else
    //logic 2
#endif
//pre Xcode v8
Other Swift Flags(OTHER_SWIFT_FLAGS) = -D <some_key>
-D DEBUG

//from Xcode v8
Active Compilation Conditions(SWIFT_ACTIVE_COMPILATION_CONDITIONS) = <some_key>
DEBUG
yoAlex5
  • 29,217
  • 8
  • 193
  • 205
0

All great answers

I want to add that if you'd like to create your own more complex macros (like you could in C++ for example) I suggest looking into Code Snippets

You could autocomplete-aly have it output things like this with the placeholders already in place:

#if DEBUG
<#DEBUGCODE#>
#else
#error("Not for Release!")
#endif

Where <#DEBUGCODE#> Generates a placeholder to fill in

Oded Ben Dov
  • 9,936
  • 6
  • 38
  • 53
-1

I'm working in a mixed language code base where the obj-c code uses a macro to send debug messages to the console (and that macro relies on our debug preprocessor flag). I wanted to be able to call that same macro in the swift code...

  1. I created a class method on one of my obj-c classes that is a wrapper around that macro.
  2. I added that obj-c header to our bridge header file.
  3. Now my swift code calls that class method as a "proxy" to the obj-c macro.

It's mildly annoying that I can't just call the macro straight up in the swift code, but at least now I only have one place in the project to worry about turning my debug flag on/off.

ghostatron
  • 2,620
  • 23
  • 27