Show debug messages if argument is set - ios

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.

Related

How do you check if SwiftUI is in preview mode?

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.

how to conditionally compile swift according to the branch [duplicate]

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 ...
}

Access Environment Variables from Swift [duplicate]

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 ...
}

string value always shows nil in objective C

I have upgraded to Xcode 5.0. And when I run an app in debug mode and try to print an NSString value in console, it gives me the below error. Any ideas?
error: warning: couldn't get cmd pointer (substituting NULL): Couldn't load '_cmd' because its value couldn't be evaluated
Couldn't materialize struct: the variable 'stringValue' has no location, it may have been optimized out
Errored out in Execute, couldn't PrepareToExecuteJITExpression
Here is the code:
NSString *stringValue = [[self.responseArray objectAtIndex:i] valueForKey:#"merchant_name"];
The reason is stated in the error message: it may have been optimized out.. this means that you are compiling and running your code in an optimized manner.
you gotta change your compiler optimization level from Fastest,Smallest to none:
go to your target build settings
search for optimization level
change it to none (whatever mode you are in ie debugging, distribution or release)
profit
do the same for your project settings
Make sure you are in debug mode. Go Edit Scheme > Build Configuration > Debug
You might be trying to debug in the "release Scheme". Go to "Product/Scheme/Edit Scheme" and pick the "run" in the left panel. Then change the build configuration to "debug".
One alternate answer: instead of fixing "it may have been optimized out" by removing the optimization, you can stop it from being optimized by using the variable.
So, in your question, if you do something with stringValue:
NSString *stringValue = [[self.responseArray objectAtIndex:i] valueForKey:#"merchant_name"];
NSLog(#"%#", stringValue);
stringValue will no longer be optimized out because you're using it.
If you want to see all instances of optimized-out variables in your project, Use Product -> Analyze to analyze your project. Any "Dead store" warnings (in which a value is stored, and never read) will be optimized out at compile time if you have compiler optimization turned on.

How to know if NSAssert is disabled in release builds?

I often saw "assert " in iOS code, I google it, and got to know it assert true or false.
I want to know if this will auto disable in release mode?
Update: Verified this works in Xcode 8 as well.
In Xcode 7, go into the project build settings and search for "Assert" in the search bar. This shows section "Apple LLVM 7.0 - Preprocessing" section. There is a setting named "Enable Foundation Assertions".
I have successfully enabled/disabled NSAssert from there.
Use NSAssert() and its companions.
in the project define NS_BLOCK_ASSERTIONS for your release configuration.
Xcode 4 tremplates disable NSAsserts in the release configuration. It adds
-DNS_BLOCK_ASSERTIONS=1
to "Other C Flags" for "Release".
From the documentation:
Assertions are disabled if the preprocessor macro NS_BLOCK_ASSERTIONS is defined.
The NSAssert macro evaluates the condition and serves as a front end to the assertion handler.
Each thread has its own assertion handler, which is an object of class NSAssertionHandler. When invoked, an assertion handler prints an error message that includes the method and class names (or the function name). It then raises an NSInternalInconsistencyException exception. If condition evaluates to NO, the macro invokes handleFailureInMethod:object:file:lineNumber:description: on the assertion handler for the current thread, passing desc as the description string.
This macro should be used only within Objective-C methods.
I will here provide a meta-answer:
Both #CocoaFu and #dasblinkenlight are correct. NS_BLOCK_ASSERTIONS turns off NSAssert() and NDEBUG turns off assert(). You need both if you use both.
As Zaph said -DNS_BLOCK_ASSERTIONS=1 is set for release. However, if you wanted to check this.
First observe in the docs that NSAssert is disabled by the macro NS_BLOCK_ASSERTIONS. Then add this to the build and observe it complies ok:
#ifdef NS_BLOCK_ASSERTIONS
#error Error - NS_BLOCK_ASSERTIONS is defined
#endif
Then change the scheme to release (cmd - shift - <)
Then observe that the build fails. Therefore NS_BLOCK_ASSERTIONS is defined meaning NSAsserts are disabled.
Asserts are conditionally compiled out of your code when NDEBUG is defined. If you define NDEBUG=1 in the corresponding build settings section, you will deactivate asserts in your code regardless of the release or debug mode.
Here's what I do at the top of my main():
#if defined(NDEBUG)
{
// The assertion code below should be compiled out of existence in a release
// build. Log an error and abort the program if it is not.
bool ok = true;
NSCAssert(ok = false, #"NS assertions should be disabled but are not");
if (!ok)
{
NSLog(#"Detected release build but NS_BLOCK_ASSERTIONS is not defined");
return -1;
}
}
#endif
Note that since main() is a C function and not an Objective-C function, NSCAssert is used above rather than NSAssert. (NSAssert expects self to be valid.)
Now, as of Xcode 6, the setting is ENABLE_NS_ASSERTIONS, which is set to 1 for Debug configurations and 0 for Release, by default.
You can opt into it for Release builds on the command line by passing the ENABLE_NS_ASSERTIONS=1 argument, which I'm doing for running unit tests that check for assertion conditions, but otherwise should run with the DEBUG flag off.

Resources