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?
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?
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.
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:
Here\'s how you check for target:
#if BANANA
print(\"We have a banana\")
#elseif MELONA
print(\"Melona\")
#else
print(\"Kiwi\")
#endif
Tested using Swift 2.2
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:
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.
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/
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
)_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,
-D DEBUG
flag to use it✗ 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)
@testable
attribute, fate uncertain on future Swift.✗ Using in if/else will always generate a \"Will never be executed\" warning.
Use Active Compilation Conditions setting in Build settings / Swift compiler - Custom flags.
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.
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
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
.
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
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,
-D DEBUG
flag to use it✓ 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.
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.
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.
XCODE 9 AND ABOVE
#if DEVELOP
//
#elseif PRODCTN
//
#else
//
#endif
![In Xcode 8 & above go to build setting -> search for custom flags ]1
In code
#if Live
print(\"Live\")
#else
print(\"debug\")
#endif
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.
func inDebugBuilds(_ code: () -> Void) {
assert({ code(); return true }())
}
Source