Best Practices for Xcode Configurations

When you create a project, Xcode gives you Development and Release configurations. The former has DEBUG set to 1 and the optimizer turned off. The latter uses an optimizer setting of -Os (optimize for speed and size together), which is what I always use on submitted apps. Together these are not enough to thoroughly develop and test your app! I just cannot emphasize enough how important it is to add more configurations, and to use them judiciously.

You should add two new configurations, Distribution and ReleaseWithAsserts. The former is used to build the release that gets put into the store (or otherwise distributed to customers), and the latter is the one you will use most during development.

  • Deployment: a Release clone, it adds a define to the Preprocessor Macros DEPLOYMENT, so that code that does say push notifications uses the proper information. It also has NS_BLOCK_ASSERTIONS=1 and NDEBUG, which disable NSAsserts() and asserts() respectively.
  • Release: used for ad hoc or local testing, and it may pull in the development push server or modify some settings necessary for ad hoc testing. As provided by Apple but with the two assertion flags: NS_BLOCK_ASSERTIONS=1 and NDEBUG.
  • ReleaseWithAsserts: a pure Release clone.
  • ReleaseWithAssertsUnitTesting: a  Release clone with a UNIT_TESTING flag.
  • Debug: as provided by Apple.

Screen Shot 2013-04-25 at 12.25.13 PM

The reasoning is as follows. Sprinkle your code with as many asserts()/NSAsserts() as possible, to catch logic and pure coding errors as soon as possible. These will only be compiled into the code for Debug and ReleaseWithAsserts.

So many developers I’ve met use Debug for all development, and only switch to Release when done. This is a huge mistake as the compiled code is hugely different between optimized and raw unoptimized code. Particularly now with ARC, so many retains and releases obviated by ARC stay in the code. Debug compiled code runs slower, uses bloated machine code, and is many ways is quite different from what you will ultimately ship. ARC issues may never appear with Debug, only showing themselves at the last minute when you test with Release. Do yourself a huge favor and only use Debug for its true purpose: working with the debugger tracking down crashes or bugs.

The reason you want Debug for bug resolution is that truely every line of code in your program will match some machine code. With the optimizer, the one to one relationship is lost, and the compiler can reorganize your code and even remove some of it. When you need Debug you need it!

Also, so now that you have the assert flags to differentiate tested or distributed code, you can add other checks and/or logs into the code, by wrapping it with #ifndef NDEBUG (a double negative!).

I also add a line to any code wrapped in #ifdef DEPLOYMENT to issue a warning – so when building it I know absolutely that the code has been in fact compiled into the binary:

#warning Using Deployment Push Notification


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s