Conditional compilation for debug or distribution builds

A common requirement is to be able to conditionally compile different settings depending on whether you are building a debug version of an app or building for distribution. Situations where this is useful include the following:

  • Removing NSLog statements from a distribution build
  • Connecting to test or production services
  • Only enabling analytics packages for distribution builds so that testing does not generate false usage data.
  • Enabling/disabling functionality to allow testing of In App purchases, etc.

I find the easiest way to do this is to set compiler flags in the different build configurations. The required field in the target build settings can be found in the GCC 4.2 - Language section and is called Other C Flags:

So for example, if you have Debug, Release and Distribution build configurations. You might set a Debug flag in the build settings of the Debug configuration as follows:

If you do not see the Other C Flags setting, ensure you have your active SDK set to the Device. I like to prefix my symbols with a unique code (replace XYZ with something specific to you) so as to avoid conflicts with any frameworks or libraries that may also define the same symbols. With the symbol defined I can now use it to define some debug statements in my Prefix.pch:

// Conditional NSLog statement
#ifdef XYZ_DEBUG
#define DLog(...) NSLog(__VA_ARGS__)
#else
#define DLog(...) do {} while(0)
#endif

Now in place of NSLog calls I can use DLog and when I build for distribution all the log statements will be removed from the code since that configuration will omit the debug flag. The Distribution build settings would define the compiler flags as follows:

So if I need different keys, credentials or URLs for production and development I can write something like this:

#ifdef XYZ_PRODUCTION
// Keys for Production environment
static NSString * _username = @"myuser";
static NSString * _password = @"topsecret";
#else
// Keys for Development environment
static NSString * _username = @"test";
static NSString * _password = @"test";
#endif

The compiler flags for the release build configuration are left empty since it is neither a distribution build or a debug build.

This technique can also be used if you want to build free and premium versions of your application from the same Xcode project. Define two separate targets (free and premium) and set a compiler flag in the premium version that you can use to include the extra functionality in the premium version of the app.