800

In C/C++/Objective C you can define a macro using compiler preprocessors. Moreover, you can include/exclude some parts of code using compiler preprocessors.

#ifdef DEBUG
    // Debug-only code
#endif

Is there a similar solution in Swift?

NoodleOfDeath
  • 13,338
  • 21
  • 69
  • 100
mxg
  • 19,703
  • 12
  • 56
  • 77
  • 1
    As an idea, you could put this in your obj-c bridging headers.. – Matej Jan 10 '15 at 13:50
  • 53
    You really should award an answer as you have several to choose from, and this question has gotten you a lot of up votes. – David H Aug 04 '16 at 14:00
  • @Userthatisnotauser you totally missed the point. You ask a question, you get great answers - choose one. Don’t just ignore the time and effort. – David H Jun 11 '20 at 00:56
  • 1
    @DavidH No, actually it's the other way around. My comment was just a Hitchhiker's reference about 42. I completely agree, and want to upvote it, but I can't bring myself to make the 43rd. – ReinstateMonica3167040 Jun 11 '20 at 00:58
  • 1
    @Userthatisnotauser the poster has 19k points - people voted his answers but he doesn’t seem to care about people who help him. I always always choose an answer . – David H Jun 11 '20 at 01:04
  • Check their account, it's dead. – user2875404 Jul 08 '20 at 22:22

17 Answers17

1139

Yes you can do it.

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.

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

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

You can read my original post here.


IMPORTANT NOTE: -DDEBUG=1 doesn't work. Only -D DEBUG works. Seems compiler is ignoring a flag with a specific value.

Community
  • 1
  • 1
Jean Le Moignan
  • 21,119
  • 3
  • 29
  • 37
  • 45
    This is the correct answer, although it should be noted that you can only check for the presence of the flag but not a specific value. – Charles Harley Jun 18 '14 at 08:52
  • Well, that is unpleasant. I frequently use #if 0/#else/#endif to try test code. It makes it trivial to go back and forth to test things like durability or speed. I understand the preprocessor was just another hack (on a hack on a hack). But in this case it really was a Useful Thing® – Lloyd Sargent Mar 24 '15 at 18:14
  • 22
    **Additional note**: On top of adding `-D DEBUG` as stated above, you also need to define `DEBUG=1` in `Apple LLVM 6.0 - Preprocessing` -> `Preprocessor Macros`. – MLQ Mar 26 '15 at 15:25
  • Can this be used to make sure some code (debug-login-credentials) are not build into the released app when building with different configurations? – Sunkas May 05 '15 at 08:13
  • 3
    @LloydSargent, "#if false" can be used in place of "#if 0" – Zmey Jul 02 '15 at 23:43
  • 40
    I couldn't get this to work until I changed the formatting to `-DDEBUG` from this answer: http://stackoverflow.com/a/24112024/747369. – Kramer Jul 23 '15 at 16:30
  • 1
    I'm experiencing a problem with this solution, or maybe a quirk. It works when I'm messing around before release. I can set my scheme to compile for either debug or release and the #if DEBUG yields the correct result. BUT, when I archive and submit the app to the App Store, it's appearing on the store in debug mode, and this is despite the fact my Archive build configuration is set to Release, which I've confirmed. I think this may not be working out on the store as people think it is. Anyone else confirm this? – John Bushnell Sep 12 '15 at 04:28
  • 1
    If you are using `.xcconfig` files you can set up these macro like this: `OTHER_SWIFT_FLAGS = $(inherited) "-D" "MAC_APP_STORE"`. – Valentin Shergin Oct 21 '15 at 03:43
  • 12
    @MattQuiros There's no need to add `DEBUG=1` to `Preprocessor Macros`, if you don't want to use it in Objective-C code. – derpoliuk Nov 16 '15 at 11:24
  • @BigRon I ended up setting a boolean in my highest level framework from my main project (where the #if DEBUG is reliable), and then all of my code within my frameworks check the boolean instead of using #if DEBUG. Of course this means the other code pathways are all still there in release, but just never execute. I haven't found a solution beyond this, but I haven't been checking lately to see if Apple may have done anything to correct it. – John Bushnell Mar 30 '16 at 06:37
  • @John A few days ago I implemented this Debug solution without your suggestion. I'll let you know how it turns out. – BigRon Mar 30 '16 at 17:14
  • This worked in our app target but not the ...AppTests even though we added them to `Apple LLVM 6.0 - Preprocessing` -> `Preprocessor Macros` as well as "Swift Compiler - Custom Flags" – finneycanhelp Apr 06 '16 at 19:14
  • 1
    Is there also something like `#ifndef DEBUG` ? (if not debug...) – Daniel Apr 22 '16 at 09:43
  • 9
    @Daniel You can use standard boolean operators (ex: ` #if !DEBUG ` ) – Jean Le Moignan Apr 22 '16 at 13:48
  • is the formatting `-D DEBUG` (with space) or `-DDEBUG` (no space)? if you also need to define DEBUG=1 as @MattQuiros said, shouldn't the answer be updated? – Crashalot May 12 '16 at 16:51
  • @MattQuiros it seems to work without defining `DEBUG=1` in `Preprocessor Macros`? are you sure this is still the case? – Crashalot May 12 '16 at 16:57
  • To be very clear about this flag: the naming DEBUG is conventional, not something that is predefined by Xcode or the preprocessor. Set -DDEBUG for the Debug configuration in "other Swift flags". Set something else, e.g. -DRELEASE for the Release configuration. – ff10 Jul 26 '16 at 12:43
  • 2
    The no-space version (`-DDEBUG`) works for me, but the missing piece to this trick is that you need to add this in the "Swift Compiler - Custom Flags" section of the *Target*, not the section of this name of the *Project* settings. – J. Cocoe Sep 10 '16 at 00:37
  • It's -D Debug for Objective C projects and D- Debug for Swift projects. – harmeet07 Oct 03 '16 at 11:17
  • Remember this needs to be defined for the specific framework/extension that use it! So if you have a keyboard/today extension define it there. If you have some other kind of framework same thing. This might only be necessary if the main target is objective c... – Warpzit Oct 21 '16 at 10:31
  • 2
    As of Xcode 9 GM I didn't have to add any flags to the build settings as I saw that `DEBUG` was already included under `Active Compilation Conditions` under `Swift Compiler-Custom Flags`. However, I am not sure if it is like this by default (when you create a new project) or was placed there by CocoaPods. – Stunner Sep 13 '17 at 05:05
  • 4
    @Stunner It's not placed by CocoaPods. I'm using Carthage and the flag is here. Seems to be the default as of Xcode 9. – Benjamin Jan 07 '18 at 07:36
  • how do you use the value for a since it is defined inside the block – Swati Mar 21 '18 at 10:31
  • These are NOT preprocessor definitions, and Swift has no preprocessor. These are compile-time attributes, and build-configuration attributes. – Motti Shneor Jun 18 '18 at 14:42
  • How can I test if it works? In Objective-C I used to write sth into the code that it gives a compiler error when it's in the right section but here this doesn't work anymore – Stephan Boner Jul 25 '18 at 11:41
  • but it's a very ugly solution, it's better to use `if _isDebugAssertConfiguration() { ... }`, this one is more similar to Andorid's `if (BuildConfig.DEBUG) { ... }` – user924 Apr 30 '19 at 19:13
  • Apple Docs link above is dead, currently this will get you to one (not sure if it's the same one) https://docs.swift.org/swift-book/ReferenceManual/Statements.html – bshirley Aug 24 '20 at 19:24
375

As stated in Apple Docs

The Swift compiler does not include a preprocessor. Instead, it takes advantage of compile-time attributes, build configurations, and language features to accomplish the same functionality. For this reason, preprocessor directives are not imported in Swift.

I've managed to achieve what I wanted by using custom Build Configurations:

  1. Go to your project / select your target / Build Settings / search for Custom Flags
  2. For your chosen target set your custom flag using -D prefix (without white spaces), for both Debug and Release
  3. Do above steps for every target you have

Here's how you check for target:

#if BANANA
    print("We have a banana")
#elseif MELONA
    print("Melona")
#else
    print("Kiwi")
#endif

enter image description here

Tested using Swift 2.2

Daniel
  • 17,803
  • 7
  • 74
  • 142
Andrej
  • 6,609
  • 4
  • 34
  • 49
  • 4
    1.with white space work also, 2.should set the flag only for Debug? – c0ming Apr 25 '16 at 02:45
  • 3
    @c0ming it depends on your needs, but if you want something to happen only in debug mode, and not in release, you need to remove -DDEBUG from Release. – cdf1982 Jul 17 '16 at 19:51
  • 1
    After i set the custom flag `-DLOCAL`, on my `#if LOCAl #else #endif`, it falls into the `#else` section. I duplicated the original target `AppTarget` and rename it to `AppTargetLocal` & set its custom flag. – Perwyl Liu Jul 20 '16 at 06:48
  • @PerwylLiu Yes, in that way you can actually set different flags for different targets, if you have more than one. Just make sure to not forghet to set the flags for other targets. – Andrej Jul 20 '16 at 06:52
  • 3
    @Andrej do you happen to know how to make XCTest recognise the custom flags as well? I realise it falls into `#if LOCAL `, the intended result when i run with the simulator and falls into `#else ` during testing. I want it to falls into `#if LOCAL` as well during testing. – Perwyl Liu Jul 20 '16 at 07:05
  • 3
    This should be the accepted answer. The current accepted answer is incorrect for Swift as it only applies to Objective-C. – miken.mkndev Sep 03 '16 at 11:01
  • @Andrej could you edit the answer to use the `Active Compilation Conditions` instead of `Other Swift Flags`. Now there is more granular control based on the configuration – user1046037 Aug 02 '17 at 12:09
  • Also no need to prefix `D` if you use `Active Compilation Conditions` – user1046037 Aug 02 '17 at 12:15
181

In many situations, you don't really need conditional compilation; you just need conditional behavior that you can switch on and off. For that, you can use an environment variable. This has the huge advantage that you don't actually have to recompile.

You can set the environment variable, and easily switch it on or off, in the scheme editor:

enter image description here

You can retrieve the environment variable with NSProcessInfo:

    let dic = NSProcessInfo.processInfo().environment
    if dic["TRIPLE"] != nil {
        // ... do secret stuff here ...
    }

Here's a real-life example. My app runs only on the device, because it uses the music library, which doesn't exist on the Simulator. How, then, to take screen shots on the Simulator for devices I don't own? Without those screen shots, I can't submit to the AppStore.

I need fake data and a different way of processing it. I have two environment variables: one which, when switched on, tells the app to generate the fake data from the real data while running on my device; the other which, when switched on, uses the fake data (not the missing music library) while running on the Simulator. Switching each of those special modes on / off is easy thanks to environment variable checkboxes in the Scheme editor. And the bonus is that I can't accidentally use them in my App Store build, because archiving has no environment variables.

Crashalot
  • 31,452
  • 56
  • 235
  • 393
matt
  • 447,615
  • 74
  • 748
  • 977
  • For some reason my environment variable returned as nil on the second app launch – Eugene Jul 25 '14 at 18:31
  • 66
    **Watch out**: Environment Variables are set for all build configurations, they can't be set for individual ones. So this is **not** a viable solution if you need the behaviour to change depending on whether it's a release or a debug build. – Eric Jun 10 '15 at 15:50
  • 8
    @Eric Agreed, but they are not set for all scheme actions. So you could do one thing on build-and-run and a different thing on archive, which is often the real-life distinction you want to draw. Or you could have multiple schemes, which also a real-life common pattern. Plus, as I said in my answer, switching environment variables on and off in a scheme is easy. – matt Jun 10 '15 at 15:54
  • @matt: I see. But how do you set an environmental variable for the Archive scheme action? I don't see an Arguments tab there... (Xcode 6.3.2) – Eric Jun 10 '15 at 16:00
  • @Eric added a real-life example (actually came up yesterday) to my answer. – matt Jun 11 '15 at 15:27
  • @Eric but one can create different schemes (e.g., two or more per target) with different environmental variables, right? – Dan Rosenstark Jul 16 '15 at 21:33
  • 11
    Environment variables do NOT work in archive mode. They are only applied when the app is launched from XCode. If you try to access these on a device, the app will crash. Found out the hard way. – iupchris10 Sep 14 '15 at 21:10
  • 3
    @iupchris10 "Archiving has no environment variables" are the last words of my answer, above. That, as I say in my answer, is _good_. It's the _point_. – matt Sep 14 '15 at 21:24
  • 1
    This is exactly the correct solution for the XCTest case, where you want a default behavior when the application is running in the simulator, but you want to strictly control the behavior in the tests. – Stan Jun 02 '17 at 17:57
  • Related to @iupchris10 comment - I'd like to clarify that in your project you can leave the code that tries to access the environment variables, even if you archive the app. When you're running an app that has been archived you get `nil` for the expected `String?` . So if you deal properly with the optional result it wont crash your app. Here's the Xcode9 snippet: `let myCustomVar = ProcessInfo.processInfo.environment["MY_CUSTOM_VAR"]` . `myCustomVar`. – Andrej Jan 25 '18 at 13:27
  • @Andrej I think that's what my answer says (illustrating with `if dic["TRIPLE"] != nil`) but thanks for underlining the point. – matt Jan 25 '18 at 16:25
  • extension ProcessInfo{ var isDebug : Bool{ get{ return self.environment["DEBUG"] == "1" } } } – Benny Davidovitz Jan 13 '19 at 11:55
  • Swift actually has a compiler flag you can use to see if the app is running on a simulator: `#if targetEnvironment(simulator)` – Peter Schorn Aug 05 '20 at 16:49
  • @PeterSchorn Correct. My answer says that sometimes that's not what's needed. What you're saying is true _and_ what I'm saying is true. Ain't life grand? – matt Aug 05 '20 at 16:56
  • @matt I didn't mean to imply that my answer contradicted anything you said. I *do* agree with everything you said. My answer is purely additive. I just didn't see this compiler flag mentioned anywhere else, so I decided post a comment about it. – Peter Schorn Aug 05 '20 at 17:36
  • Not enough people realize the efficiencies and workflow ramifications of adding a million re-compilation requirements... thank you for this @matt – Alex Moore Nov 20 '20 at 13:13
173

A major change of ifdef replacement came up with Xcode 8. i.e use of Active Compilation Conditions.

Refer to Building and Linking in Xcode 8 Release note.

New build settings

New setting: SWIFT_ACTIVE_COMPILATION_CONDITIONS

“Active Compilation Conditions” is a new build setting for passing conditional compilation flags to the Swift compiler.

Previously, we had to declare your conditional compilation flags under OTHER_SWIFT_FLAGS, remembering to prepend “-D” to the setting. For example, to conditionally compile with a MYFLAG value:

#if MYFLAG1
    // stuff 1
#elseif MYFLAG2
    // stuff 2
#else
    // stuff 3
#endif

The value to add to the setting -DMYFLAG

Now we only need to pass the value MYFLAG to the new setting. Time to move all those conditional compilation values!

Please refer to below link for more Swift Build Settings feature in Xcode 8: http://www.miqu.me/blog/2016/07/31/xcode-8-new-build-settings-and-analyzer-improvements/

DShah
  • 9,510
  • 10
  • 66
  • 123
  • Is there anyway to disable a set Active Compilation Conditions at build time ? I need to disable the DEBUG condition when building the debug configuration for testing. – Jonny Sep 01 '17 at 02:56
  • 1
    @Jonny The only way I've found is to create a 3rd build config for the project. From the Project > Info tab > Configurations, hit '+', then duplicate Debug. You can then customize the Active Compilation Conditions for this config. Don't forget to edit your Target > Test schemes to use the new build configuration! – matthias Oct 10 '17 at 21:04
  • 1
    This should be the correct answer..its the only thing that worked for me on xCode 9 using Swift 4.x ! – shokaveli Apr 11 '18 at 21:19
  • 1
    BTW, In Xcode 9.3 Swift 4.1 DEBUG is already there in Active Compilation Conditions and you don't have to add anything to check for DEBUG configuration. Just #if DEBUG and #endif. – Denis Kutlubaev Jun 03 '18 at 13:10
  • I think this is both off-topic, and a bad thing to do. you do not want to disable Active Compilation Conditions. you need a new and different configuration for testing - that will NOT have the "Debug" tag on it. Learn about schemes. – Motti Shneor Jun 18 '18 at 07:20
95

As of Swift 4.1, if all you need is just check whether the code is built with debug or release configuration, you may use the built-in functions:

  • _isDebugAssertConfiguration() (true when optimization is set to -Onone)
  • _isReleaseAssertConfiguration() (true when optimization is set to -O) (not available on Swift 3+)
  • _isFastAssertConfiguration() (true when optimization is set to -Ounchecked)

e.g.

func obtain() -> AbstractThing {
    if _isDebugAssertConfiguration() {
        return DecoratedThingWithDebugInformation(Thing())
    } else {
        return Thing()
    }
}

Compared with preprocessor macros,

  • ✓ You don't need to define a custom -D DEBUG flag to use it
  • ~ It is actually defined in terms of optimization settings, not Xcode build configuration
  • ✗ Undocumented, which means the function can be removed in any update (but it should be AppStore-safe since the optimizer will turn these into constants)

  • ✗ Using in if/else will always generate a "Will never be executed" warning.

kennytm
  • 469,458
  • 94
  • 1,022
  • 977
  • 1
    Are these built-in functions evaluated at compile time or runtime? – ma11hew28 Feb 28 '16 at 19:59
  • @MattDiPasquale Optimization time. `if _isDebugAssertConfiguration()` will be evaluated to `if false` in release mode and `if true` is debug mode. – kennytm Mar 01 '16 at 04:52
  • 2
    I can't use these functions to opt out some debug-only variable in release, though. – Franklin Yu Apr 12 '16 at 01:04
  • 3
    Are these functions documented somewhere? – Tom Harrington May 02 '16 at 17:27
  • @TomHarrington It wasn't publicly documented anywhere. The source code is in https://github.com/apple/swift/blob/master/stdlib/public/core/AssertCommon.swift. There is a [Draft SE proposal](http://comments.gmane.org/gmane.comp.lang.swift.evolution/12140) to replace these functions by a better syntax like `#if config(debug)`. – kennytm Jun 13 '16 at 16:28
  • 7
    As of Swift 3.0 & XCode 8, these functions are invalid. – CodeBender Nov 03 '16 at 21:38
  • @CodeBender On Swift 3.1 only `_isReleaseAssertConfiguration()` is invalid, the other two becomes public again [due to a bug](https://github.com/apple/swift/commit/d0697f2ac1092f74548c4df348194a3ee9ea7cda). – kennytm May 26 '17 at 06:01
  • @kennytm Do you know if these functions are still available as of Swift 4, and newer? can you please update your answer? – Motti Shneor Jun 18 '18 at 07:23
  • @MottiShneor Still exists on 4.1. Updated. – kennytm Jun 18 '18 at 09:10
90

Xcode 8 and above

Use Active Compilation Conditions setting in Build settings / Swift compiler - Custom flags.

  • This is the new build setting for passing conditional compilation flags to the Swift compiler.
  • Simple add flags like this: ALPHA, BETA etc.

Then check it with compilation conditions like this:

#if ALPHA
    //
#elseif BETA
    //
#else
    //
#endif

Tip: You can also use #if !ALPHA etc.

Jakub Truhlář
  • 15,319
  • 7
  • 65
  • 73
80

There is no Swift preprocessor. (For one thing, arbitrary code substitution breaks type- and memory-safety.)

Swift does include build-time configuration options, though, so you can conditionally include code for certain platforms or build styles or in response to flags you define with -D compiler args. Unlike with C, though, a conditionally compiled section of your code must be syntactically complete. There's a section about this in Using Swift With Cocoa and Objective-C.

For example:

#if os(iOS)
    let color = UIColor.redColor()
#else
    let color = NSColor.redColor()
#endif
rickster
  • 118,448
  • 25
  • 255
  • 308
  • 36
    "For one thing, arbitrary code substitution breaks type- and memory-safety." Doesn't a pre-processor do its work before the compiler does (hence the name)? So all these checks could still take place. – Thilo Jun 04 '14 at 01:42
  • 10
    @Thilo I think what it breaks is IDE support – Aleksandr Dubinsky Jun 04 '14 at 20:37
  • 1
    I think what @rickster is getting at is that C Preprocessor macros have no understanding of type and their presence would break Swift's type requirements. The reason macros work in C is because C allows implicit type conversion, which means you could put your `INT_CONST` anywhere a `float` would be accepted. Swift would not allow this. Also, if you could do `var floatVal = INT_CONST` inevitably it would breakdown somewhere later when the compiler expects an `Int` but you use it as a `Float` (type of `floatVal` would be inferred as `Int`). 10 casts later and its just cleaner to remove macros... – Ephemera Jun 07 '14 at 01:45
  • I'm trying to use this but it doesn't seem to work, it's still compiling the Mac code on iOS builds. Is there another setup screen somewhere that has to be tweaked? – Maury Markowitz Feb 21 '15 at 21:59
  • 1
    @Thilo you are correct - a pre-processor does not break any type or memory safety. – tcurdt Jun 10 '16 at 09:47
  • You can think of it as a fancy search-and-replace/templating before the compilation step. Given it's a source level transformation it only breaks things if the result of the transformation breaks things. Which is why @Ephemera's example and conclusion is not correct like that. – tcurdt Jun 10 '16 at 09:56
  • This seems to be the only solution that is working on Xcode 10.1 Swift 4.2! – nickgzzjr Jan 29 '19 at 15:01
55

isDebug Constant Based on Active Compilation Conditions

Another, perhaps simpler, solution that still results in a boolean that you can pass into functions without peppering #if conditionals throughout your codebase is to define DEBUG as one of your project build target's Active Compilation Conditions and include the following (I define it as a global constant):

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

isDebug Constant Based on Compiler Optimization Settings

This concept builds on kennytm's answer

The main advantage when comparing against kennytm's, is that this does not rely on private or undocumented methods.

In Swift 4:

let isDebug: Bool = {
    var isDebug = false
    // function with a side effect and Bool return value that we can pass into assert()
    func set(debug: Bool) -> Bool {
        isDebug = debug
        return isDebug
    }
    // assert:
    // "Condition is only evaluated in playgrounds and -Onone builds."
    // so isDebug is never changed to true in Release builds
    assert(set(debug: true))
    return isDebug
}()

Compared with preprocessor macros and kennytm's answer,

  • ✓ You don't need to define a custom -D DEBUG flag to use it
  • ~ It is actually defined in terms of optimization settings, not Xcode build configuration
  • Documented, which means the function will follow normal API release/deprecation patterns.

  • ✓ Using in if/else will not generate a "Will never be executed" warning.

Jon Willis
  • 6,839
  • 4
  • 38
  • 51
52

My two cents for Xcode 8:

a) A custom flag using the -D prefix works fine, but...

b) Simpler use:

In Xcode 8 there is a new section: "Active Compilation Conditions", already with two rows, for debug and release.

Simply add your define WITHOUT -D.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
ingconti
  • 9,213
  • 2
  • 51
  • 39
39

Moignans answer here works fine. Here is another peace of info in case it helps,

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

You can negate the macros like below,

#if !RELEASE
    let a = 2
#else
    let a = 3
#endif
Sazzad Hissain Khan
  • 29,428
  • 20
  • 134
  • 192
27

In Swift projects created with Xcode Version 9.4.1, Swift 4.1

#if DEBUG
#endif

works by default because in the Preprocessor Macros DEBUG=1 has already been set by Xcode.

So you can use #if DEBUG "out of box".

By the way, how to use the condition compilation blocks in general is written in Apple's book The Swift Programming Language 4.1 (the section Compiler Control Statements) and how to write the compile flags and what is counterpart of the C macros in Swift is written in another Apple's book Using Swift with Cocoa and Objective C (in the section Preprocessor Directives)

Hope in future Apple will write the more detailed contents and the indexes for their books.

Vadim Motorine
  • 2,499
  • 1
  • 9
  • 7
21

XCODE 9 AND ABOVE

#if DEVELOP
    //
#elseif PRODCTN
    //
#else
    //
#endif
kuzdu
  • 5,943
  • 1
  • 37
  • 56
midhun p
  • 1,351
  • 11
  • 20
12

There are some processors that take an argument and I listed them below. you can change the argument as you like:

#if os(macOS) /* Checks the target operating system */

#if canImport(UIKit) /* Check if a module presents */

#if swift(<5) /* Check the Swift version */

#if targetEnvironment(simulator) /* Check envrionments like Simulator or Catalyst */

#if compiler(<7) /* Check compiler version */

Also, You can use any custom flags like DEBUG or any other flags you defined

#if DEBUG
print("Debug mode")
#endif
Mojtaba Hosseini
  • 47,708
  • 12
  • 157
  • 176
7

After setting DEBUG=1 in your GCC_PREPROCESSOR_DEFINITIONS Build Settings I prefer using a function to make this calls:

func executeInProduction(_ block: () -> Void)
{
    #if !DEBUG
        block()
    #endif
}

And then just enclose in this function any block that I want omitted in Debug builds:

executeInProduction {
    Fabric.with([Crashlytics.self]) // Compiler checks this line even in Debug
}

The advantage when compared to:

#if !DEBUG
    Fabric.with([Crashlytics.self]) // This is not checked, may not compile in non-Debug builds
#endif

Is that the compiler checks the syntax of my code, so I am sure that its syntax is correct and builds.

Rivera
  • 10,182
  • 3
  • 49
  • 96
4

![In Xcode 8 & above go to build setting -> search for custom flags ]1

In code

 #if Live
    print("Live")
    #else
    print("debug")
    #endif
sachin_kvk
  • 67
  • 9
  • 1
    You have hit on it here! Swift #if looks at custom flags NOT preprocessor macros. Please update your answer with the content from the link, often times links will break after a while – Dale May 18 '19 at 07:15
4
func inDebugBuilds(_ code: () -> Void) {
    assert({ code(); return true }())
}

Source

Adam Smaka
  • 4,239
  • 2
  • 38
  • 38
  • 1
    This isn't conditional compilation. While useful , its just a plain old runtime conditional. The OP is asking after compiletime for metaprogramming purposes – Shayne Jan 18 '19 at 02:00
  • 3
    Just add `@inlinable` in front of `func` and this would be the most elegant and idiomatic way for Swift. In release builds your `code()` block will be optimized and eliminated altogether. A similar function is used in Apple's own NIO framework. – mojuba May 12 '19 at 08:42
2

This builds on Jon Willis's answer that relies upon assert, which only gets executed in Debug compilations:

func Log(_ str: String) { 
    assert(DebugLog(str)) 
}
func DebugLog(_ str: String) -> Bool { 
    print(str) 
    return true
}

My use case is for logging print statements. Here is a benchmark for Release version on iPhone X:

let iterations = 100_000_000
let time1 = CFAbsoluteTimeGetCurrent()
for i in 0 ..< iterations {
    Log ("⧉ unarchiveArray:\(fileName) memoryTime:\(memoryTime) count:\(array.count)")
}
var time2 = CFAbsoluteTimeGetCurrent()
print ("Log: \(time2-time1)" )

prints:

Log: 0.0

Looks like Swift 4 completely eliminates the function call.

Warren Stringer
  • 1,349
  • 1
  • 12
  • 11
  • Eliminates, as in removes the call in its entirety when not in debug - due to the function being empty? That would be perfect. – Johan Apr 24 '18 at 06:40