Is there a way to check if a SwiftUI app is in preview mode? For example, you can check if your app is in development or production mode using #if DEBUG. Can you do something similar to check if you're previewing or not?
You can detect this using ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"]. The value will be "1" at runtime when running in the canvas.
If you like me were looking for an environment variable to use in build scripts which xcode sets when building for SwiftUI previews, it turned out to be ENABLE_PREVIEWS.
SwiftUI were pausing preview when my script updated Info.plist file. To fix that I exit the script at certain point if we are in preview build.
if [ "${ENABLE_PREVIEWS}" = "YES" ]; then
exit 0;
fi
Though there is no compilation flag currently available for checking if the active build is intended for the Previews Canvas, I would still recommend using a compiler directive over a runtime check, if it can meet your needs.
For example, this check resolves to true for both the simulator and Previews:
#if targetEnvironment(simulator)
// Execute code only intended for the simulator or Previews
#endif
Negate the condition if you want your code to only execute on physical devices (such as camera-related operations that are otherwise guaranteed to fail).
The runtime check for whether your code is executing for Previews (as given in the accepted answer) probably does not add significant performance overhead, but it still feels a little gross to ship that code IMO. So it's worth at least considering first if your situation requires that level of specificity. If it does, I'd recommend wrapping that code in a compiler check to remove it from release builds.
If you don't want to rely on the ProccessInfo value you can always design your own environment variable in SwiftUI.
import SwiftUI
private struct IsPreviewKey: EnvironmentKey {
static let defaultValue = false
}
extension EnvironmentValues {
var isPreview: Bool {
get { self[IsPreviewKey.self] }
set { self[IsPreviewKey.self] = newValue }
}
}
Then when you create your preview inject the variable
MyView().environment(\.isPreview, true)
and you can use it in your view like this:
struct MyView: View {
#Environment(\.isPreview) var isPreview
}
I usually have a method that generates all the various versions for a preview (light mode, dark mode, iPad, iPhone, …) so I inject it there to all of the previews.
Related
Is it possible to disable RealityKit/ARKit/AR when building for xcode Simulator? So that I can style/change non-ar related pages.
I want to change my home page, adding different sections and styling. The home page has nothing to do with ARKit/Reality Kit, but I don't have the physical device with me.
The errors say that various RealityKit/ARKit functions are undefined or don't exist. Like ARView has no member .occlusion.
The home page has no need for AR at all, is it possible to build and run the simulator, or even preview without a physical (AR Enabled) device?
You can use a Conditional Compilation Block, as explained in the Swift Programming Manual:
class SubclassedARView : ARView {
var configuration: ARWorldTrackingConfiguration()
var runOptions: ARSession.RunOptions()
init() {
#if !targetEnvironment(simulator)
// Code here is only compiled when building to a real or generic device.
// This is the place where you should add your RealityKit specific
// code, or at least the code that gives build-time errors in Xcode.
// An example of a problematic method:
session.run(configuration, options: runOptions)
#endif
}
}
(programming in swift 2)
In my iOS application I am using frameworks. I am also using the IBDesignable feature with great success. I have a bunch of views and buttons in frameworks that are IBDesignable and thus render nicely on the screen of interface builder.
Problem is that one of these views executes code when its initialized that will do an assertion if its being run just in the context of the IBDesignable interfacebuilder (executed just to render the IB screen). The reason for the assertion is valid, and really does not matter, bottom line is that I do not want to loose this functionality during "normal" operation.
My idea for a solution is to know when the code is being executed just for rendering in IB for IBDesignable mode, and thus build in a switch to not/do the assert.
I tried using the #if !TARGET_INTERFACE_BUILDER but this is not working, as this is a preprocessor directive and thus only evaluated a compile time, and the frameworks are precompiled (when used in the actual app).
I also thought about using prepareForInterfaceBuilder() but this is not applicable because the class throwing the assert has nothing todo with UIView itself.
Is there a function or any other way to check AT RUNTIME (in a framework) that your code is being run as part of IB screen rendering for IBDesignable mode?
After testing a few dozens solutions, I found the following to work reliably:
/**
Returns true if the code is being executed as part of the Interface Builder IBDesignable rendering,
so not for testing the app but just for rendering the controls in the IB window. You can use this
to do special processing in this case.
*/
public func runningInInterfaceBuilder() -> Bool {
//get the mainbundles id
guard let bundleId = NSBundle.mainBundle().bundleIdentifier else {
//if we don't have a bundle id we are assuming we are running under IBDesignable mode
return true
}
//when running under xCode/IBDesignable the bundle is something like
//com.apple.InterfaceBuilder.IBCocoaTouchPlugin.IBCocoaTouchTool
//so we check for the com.apple. to see if we are running in IBDesignable rendering mode
//if you are making an app for apple itself this would not work, but we can live with that :)
return bundleId.rangeOfString("com.apple.") != nil
}
SWIFT 4 version
/**
Returns true if the code is being executed as part of the Interface Builder IBDesignable rendering,
so not for testing the app but just for rendering the controls in the IB window. You can use this
to do special processing in this case.
*/
public func runningInInterfaceBuilder() -> Bool {
//get the mainbundles id
guard let bundleId = Bundle.main.bundleIdentifier else {
//if we don't have a bundle id we are assuming we are running under IBDesignable mode
return true
}
//when running under xCode/IBDesignable the bundle is something like
//com.apple.InterfaceBuilder.IBCocoaTouchPlugin.IBCocoaTouchTool
//so we check for the com.apple. to see if we are running in IBDesignable rendering mode
//if you are making an app for apple itself this would not work, but we can live with that :)
return bundleId.contains("com.apple.")
}
In my project i'm showing debug messages with global variable:
struct GVariables {
static let debug = false
}
if GVariables.debug {
print("Debug mode enabled")
}
But is it possible to set argument here:
and check debug argument in code. How can i do this ? And is this a correct way ?
You can get access to those launch arguments and environment variables via NSProcessInfo
if NSProcessInfo.processInfo.arguments["DEBUGRPM"] ...
That's not unreasonable and allows you to change the behavior of a compiled app which can be useful in some cases. It does however have some overhead since you're always performing this check. If you would only ever enable debug logging in a debug build then setting a value in "Swift Compiler - Custom Flags" (as shown in the question #Larme linked) and using an #if DEBUGRPM expression will give you conditionally compiled code, saving the app the work of performing a runtime if test.
Which approach is more reasonable for you will depend on how you plan to use and when you plan to toggle this behavior.
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:
Go to your project / select your target / Build Settings / search for Custom Flags
For your chosen target set your custom flag using -D prefix (without white spaces), for both Debug and Release
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
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) (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)
these once removed, but brought back to public to lack of #testable attribute, fate uncertain on future Swift.
✗ Using in if/else will always generate a "Will never be executed" warning.
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.
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
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.
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.
Moignans answer here works fine. Here is another piece 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
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
//print("Develop")
#elseif PRODUCTION
//print("Production")
#else
//
#endif
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
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 Xcode 8 & above go to build setting -> search for custom flags
]1
In code
#if Live
print("Live")
#else
print("debug")
#endif
func inDebugBuilds(_ code: () -> Void) {
assert({ code(); return true }())
}
Source
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.
Swift 5 update for matt's answer
let dic = ProcessInfo.processInfo.environment
if dic["TRIPLE"] != nil {
// ... do your secret stuff here ...
}
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:
Go to your project / select your target / Build Settings / search for Custom Flags
For your chosen target set your custom flag using -D prefix (without white spaces), for both Debug and Release
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
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) (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)
these once removed, but brought back to public to lack of #testable attribute, fate uncertain on future Swift.
✗ Using in if/else will always generate a "Will never be executed" warning.
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.
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
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.
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.
Moignans answer here works fine. Here is another piece 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
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
//print("Develop")
#elseif PRODUCTION
//print("Production")
#else
//
#endif
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
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 Xcode 8 & above go to build setting -> search for custom flags
]1
In code
#if Live
print("Live")
#else
print("debug")
#endif
func inDebugBuilds(_ code: () -> Void) {
assert({ code(); return true }())
}
Source
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.
Swift 5 update for matt's answer
let dic = ProcessInfo.processInfo.environment
if dic["TRIPLE"] != nil {
// ... do your secret stuff here ...
}