Compiling conditional code in Universal iPhone/iPad applications

Or an even better title for this post would be, “How to test your universal app in the iPhone simulator”. Since testing in the iPhone simulator requires you compile with a pre-3.2 SDK, you need to account for any 3.2 specific calls you make, here’s how you do that.

Some of you might have read Apple’s documentation about how to build a Universal iPhone/iPad binary that runs on both iPhone OS 3.2 (iPad) and iPhone OS 3.1.3 (current iPhones & iPod Touches). If you go the Universal application route you’ll undoubtedly come across the need to run different code depending on the target device. Thankfully Apple makes this quite easy; under the documentation section, “Using Runtime Checks To Create Conditional Code Paths” they offer this code snippet:

if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
//iPad specific code.
} else {
//iPhone specific code.
}

This works great if you’re developing for the iPad, but if you want to check the progress of your app with the iPhone simulator, you’ll need to compile with 3.1.3 and you’ll quickly find that Apple’s snippet will not compile for a couple reasons.

  1. The macro UI_USER_INTERFACE_IDIOM is undefined in 3.1.3.
  2. The class returned by the above macro is also non-existent.

Apple’s documentation acknowledges this problem when they say:

“Because this property is available only in iPhone OS 3.2 and later, you must determine if it is available before calling it. The simplest way to do this is to use the UI_USER_INTERFACE_IDIOM macro…”

This is a confusing section of iPad documentation because they’re recommending the developer use a new macro to avoid compilation errors with earlier SDK versions. They don’t acknowledge the fact that the macro itself doesn’t exist prior to 3.2 and will throw compilation errors.

Luckily the solution is as simple as can be. Just use the the C preprocessor’s #ifdef directive. The code below will prevent the 3.2 specific code from getting compiled by an older SDK:

BOOL iPad = NO;
#ifdef UI_USER_INTERFACE_IDIOM
iPad = (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad);
#endif
if (iPad) {
// iPad specific code here
} else {
// iPhone/iPod specific code here
}

Compilation errors and warnings are now gone and you can easily test your Universal application in both the iPad and iPhone environments.

UPDATE:

Sometimes you might need to put 3.2 specific code in the iPad code block above, this will also break compilation when you build with 3.1.3. I recently came across this when working on a location based application that called an API that was deprecated in 3.2. I could have ignored the XCode warnings and just used the deprecated API, but why risk having my app break when Apple pushes an OS update that actually strips the call? C’s ifdef directive to the rescue again…

BOOL os32plus;
#ifdef UI_USER_INTERFACE_IDIOM
os32plus = YES;
#else
os32plus = NO;
#endif
NSInteger dist;
if ([SharedData data].iPad && os32plus) {
dist = [friendLocation distanceFromLocation:locationManager.location];
} else {
dist = [friendLocation getDistanceFrom:locationManager.location];
}

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

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

Facebook photo

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

Connecting to %s