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.
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)
✗ Using in if/else will always generate a "Will never be executed" warning.
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
.
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
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
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.