Xcode - conditional development or production mode - objective-c

In searching for this I could only find info for other languages. I would like to know the best practice for writing conditional statments such as:
if (DEV_MODE){
//do something
}else{
//do the real stuff
}
And then setting DEV_MODE on or off in some global file so I only have to change that to on or off instead of changing code in multiple places. I have some ideas but am looking for advice on the best way to do this.

Try this:
#ifdef DEBUG
// do stuff
#endif
The current version of Xcode automatically sets this macro in new projects. Go to your projects Build Settings to make sure. If it's not there you have to add it yourself:

(This question is not related to Xcode.)
That said, instead of polluting your code with C-style IFs, you can use preprocessor directives to filter out debug and release mode. As far as I know, it's a common practice to define the DEBUG macro to 1 if in testing mode, and not to define it if in release mode. (Also, Xcode may define this for you, I've seen this behavior having been relied upon.) The reason is that the use of the preprocessor is more readable, since it doesn't get into the indentation, it's better separated from the code visually. To sum up, try
#ifdef DEBUG
// do debug stuff here
#else
// do release stuff here
#endif
One extra argument for preprocessor macros is that you can conditionalize the global namespace, which you couldn't within C code. I. e., with preprocessor macros, you can write
#ifdef DEBUG
int functionOne()
{
}
#else
char *functionTwo(int a)
{
}
#endif
You couldn't do this without the preprocessor.

Preprocessor variables are what you want, in your build configurations define a variable (DEV_MODE for example), and then use preprocessor checks like:
#if DEV_MODE
//dev mode code
#else
//non-dev mode code
#endif

In your project settings there is a section called "Preprocessor Macros". In that section you can add a string for your Debug build such as "DEV_MODE" or whatever you want. Then you can do your conditions you listed above and when your app gets built for "Release" or any other setting that does not contain your macro your check should fail.

Related

How to delete all NSLog's & comments from my Xcode application?

Is there anyway to delete the NSLog lines from the app by any trick/tool? I usually use NSLog's in each and every method to understand the flow of control and to know about the values of the app's variables. I also use lots of comment lines to explain the nature of methods and variables.
At some point these NSLogs and comment lines make the program hard to for me to understand. So I need to keep deleting and recreating them. Is there a way to show/hide them by any trick in Xcode?
Use the global research & replace tool (cmd-shift-f, or Edit, find, Find in Workspace)
Clic on Find, select Replace
Style => Regular expression
For the NSLogs, search
NSLog\(.*\).*$
and replace by a space.
For the comments, search
\/\/.*$
and
\/\*.*\*\/
and replace by a space.
And finish by replacing manually those ones
/* fjeizghrij
eopgfjeipgez
*/
because I don't know how to grab them safely?
EDIT :
At last, beware of this global replace because you won't be able to undo ! Copy/paste your project before, for example. You should use the preview fonctionality of the global replace too, and check each entry.
I'm not sure what the exact reason is why you want to remove the NSLog lines and comments.
if you can read the source code hard, to remove the comments, set the comments colour in the Xcode preferences to same as the background or set their font size to 1 and you won't see them when you read the code. :)
I have no idea for the NSLog, but I'm using the following way to avoid the unwanted logging in the final release.
this is a simple macro:
#ifdef DEBUG
#define DebugLog(...) NSLog(__VA_ARGS__)
#else
#define DebugLog(...) { }
#endif
I'm using the DebugLog(...) as I would use the NSLog(...) normally, and the Xcode is logging only in DEBUG mode, I don't need to remove any log when I create the release version of the app.

How to do not write log on release mode in iOS?

In my memory, there's some code that can let the NSLog not work when released.
I don't want to remove my NSLog in my code.It helps a lot on debug mode.
So I want to find a way that can leave them in my code, and also don't slow down the application when released.
Thanks for your help~~
A common way to remove all NSLog(…) call in a release is to create a macro that is between conditional preprocessor macro and compiles to nothing, something like this:
#ifdef RELEASE
# define NSLog(...) //remove loggin in production
#endif
Put this code in a .h file that is included in every other file, like a Defines.h that is #include'd in the prefix header (.pch).
RELEASE should be a preprocessor macro defined against the Release configuration in the "Build Settings" tab of your target.
Related Questions
Is it true that one should not use NSLog() on production code?
How to print out the method name and line number and conditionally disable NSLog?
You will need to replace NSLog with a custom macro.
Put for instance
#ifdef DEBUG
# define DLog(...) NSLog(__VA_ARGS__)
#else
# define DLog(...) /* */
#endif
#define ALog(...) NSLog(__VA_ARGS__)
in your prefix pch file.
Use ALog for cases where you always want log output regardless of the state of the debug flag, and DLog for log only in debug mode.
Source

Change code based on Xcode build type

When I build my app as an Adhoc, I would like to set a specific var to YES. Is it possible for my code to know what it is being built for, and change a part of the code if it is for debug or adhoc?
In the Build Settings for a target you can set preprocessor symbols based on the build type you are doing, then in your code you can test for these using #ifdef/#endif, or to test for specific values you can use #if. Look up the preprocessor in the docs.
For example, this is commonly used to define the symbol DEBUG for debug builds and then conditionally include code:
#ifdef DEBUG
NSLog(#"The value of ix is %d", ix);
#endif

How can I access a user-defined Xcode build setting?

If I added a user-defined setting in my build configuration, how can I read that setting in my Objective-C code?
I have two files in my project, debug.plist and release.plist. I want my MainApp.m file to read one of these files based on which build configuration is running. I set up a user-defined setting named "filename" in both the Debug and Release configurations to point to the appropriate file. But I don't know how my MainApp.m file can read the filename variable from the current running configuration.
Here's what I did, I'm not 100% sure if this is what you're after:
Go into the build Settings panel and choose the gear icon in the bottom left: add User-Defined Setting
Create your user defined setting, for example:
MY_LANG -> en_us
Then, in the Preprocessor Macro's setting, you can reference that value:
LANGCODE="$(MY_LANG)"
Now you can refer to LANGCODE in all your source files, and it will be whatever you filled out in your custom build setting. I realize that there's a level of indirection here, but that is intentional in my case: my XCode project contains a bunch of different targets/configurations with their own preprocessor macro's. I don't want to have to go into all of those, just to change the language code. In fact, I define the language code on the project level. I also use MY_LANG in a couple scripts, so just a preprocessor macro wouldn't do. There may be a smarter way, but this works for me.
You can access your user-defined build setting at run-time (as suggested in a comment by #JWWalker)
Add an entry to your Info.plist file, and set it to your User-defined Build Setting
MySetting -> ${MYSETTING}
Read its value from code
Objective-C
[[NSBundle mainBundle] objectForInfoDictionaryKey:#"MySetting"];
[Edit] Swift
guard let mySetting =
Bundle.main.object(forInfoDictionaryKey: "MySetting") as? String
else { print("MySetting not found") }
Swift 4
Lets say "filename" is the String you need in your app.
Add filename=YOUR_STRING to user-defined setting(for debug and release).
And add filename = $(filename) to info.plist.
Then in Swift code:
if let filename = Bundle.main.infoDictionary?["filename"] as? String {
// do stuff with filename
}
else {
// filename wasn't able to be casted to String
}
Your code can't read arbitrary build settings. You need to use preprocessor macros.
EDIT: For example, in the target settings for the Debug configuration, you could add DEBUGGING=1 in the Preprocessor Macros build setting, and not define DEBUGGING in the Release configuration. Then in your source code you could do things like:
#if DEBUGGING
use this file
#else
use the other one
#endif
I tried zmippie suggestion but it didn't work for me.
I got it working with this:
${MY_LANG}
In case anyone else is still stuck looking for how to do preprocessor macros, look for the Apple LLVM - Preprocessing section in Build Settings. Under it, you will see a section called Preprocessor Macros.
This is where by default, Xcode inserts the DEBUG=1 macro for the debug build configuration.
You can add your own here, and give them different values for debug, release and any custom build configs you may have.
To add one, double-click on the current value list for the config you want, and it'll display a nice little editor with one macro on each line. Just add your own macro name, and give it a value the same way the DEBUG one is done.
These can be checked during the preprocessor build phase using #if, #ifdef etc. to provide conditional code or values.
Hope that helps.

How do I declare a debug only statement

In C# I can use the following code to have code which only executes during debug build, how can I do the same in Xcode?
#if DEBUG
{
// etc etc
}
#endif
You can use
#ifdef DEBUG
....
#endif
You'll need to add DEBUG=1 to the project's preprocessor symbol definitions in the Debug configuration's settings as that's not done for you automatically by Xcode.
I personally prefer doing DEBUG=1 over checking for NDEBUG=0, since the latter implies that the default build configuration is with debug information which you then have to explicitly turn off, whereas 'DEBUG=1' implies turning on debug only code.
The NDEBUG symbol should be defined for you already in release mode builds
#ifndef NDEBUG
/* Debug only code */
#endif
By using NDEBUG you just avoid having to specify a -D DEBUG argument to the compiler yourself for the debug builds
DEBUG is now defined in "debug mode" by default under Project/Preprocessor Macros. So testing it always works unless you have a very old project.
However I hate the fact that it messes up the code indentation and not particularly compact. That is why I use another macro which makes life easier.
#ifdef DEBUG
#define DEBUGMODE YES
#else
#define DEBUGMODE NO
#endif
So testing the DEBUGMODE value is much more compact:
if (DEBUGMODE) {
//do this
} else {
//do that
}
My favourite:
NSTimeInterval updateInterval = DEBUGMODE?60:3600;
There is a very useful debugging technote: Technical Note TN2124 Mac OS X Debugging Magic
http://developer.apple.com/technotes/tn2004/tn2124.html#SECENV which contains lots of useful stuff for debugging your apps.
Tony