Monday, April 5, 2010

A Few More Notes on Creating Universal Apps

Now that I've had some more time with the iPad and making Universal Apps, I've realized there are some important things I left out of Saturday's post.

First, I talked about the newly defined macro UI_USER_INTERFACE_IDIOM(). There's a problem with this right now, however, because iPhone OS 3.2 was an iPad only release. Most of us are assuming that iPhone and iPad will eventually run on the same OS version, but for now they don't ,and UI_USER_INTERFACE_IDIOM() doesn't exist in 3.1.2 and earlier. So, if you're creating a Universal Binary, you can't compile code that uses it.

Instead, you need to use a different technique, which is the check the precompiler definition __IPHONE_OS_VERSION_MAX_ALLOWED, which is set to the OS version number without dots. The definition is five digits, the first is the major release number, the second and third are the minor release number, and the fourth and fifth number are the patch release number. So, 3.2 would be defined to 30200 and 3.1.2 would be defined to 30102, so here's how you should probably design your code for the time being:

#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 30200
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
NSLog(@"iPad Idiom");
else
#else
NSLog(@"iPhone Idiom");
#endif


In the future, you won't need to check this, but it won't hurt to after. Since it's resolved during pre-compilation, there are no drawbacks other than a little bit of code clutter.

I forgot to mention that there's an option to upgrade Targets to Universal under the Project menu. It will convert your target for you, and you should use it instead of manually setting the project (the way I did my first few). If the option is grayed out, make sure that your project's base SDK (not just the currently selected SDK) is iPhone Device 3.0+. If it's older, or if it can't find the Base SDK, you won't get the option.

This option will do a few things. It will update your Base SDK to 3.2 and your Deployment SDK to the original Base SDK. It will configure your targets either to create a Universal Application, or two separate applications. It will create your application's main iPad NIB for you. It will strip out the ARM6 instructions from the iPad portion of the binary.

Note: As a few people have pointed out in the comments, UI_USER_INTERFACE_IDIOM() works just fine in Universal Apps. Where you can't use it is if you want to test your universal app's iPhone functionality in the simulator. The 3.2 Simulator only works in iPad configuration, so to test the iPhone build on the simulator, you need to set the Active SDK to 3.1.2, and this check will allow your code to compile against the 3.1.2 Simulator target. If you're testing on the device, the code above is unnecessary (but also doesn't hurt anything). Sorry for the confusion.




18 comments:

Ole Begemann said...

This is not correct, Jeff. First, to clarify, universal apps for iPhone and iPad are not Universal Binaries in the PowerPC/Intel sense (where the executable actually contained two binaries). A universal app is just one binary so you can't work with conditional compilation. The check on which device you're running must happen at runtime.

Apple's UI_USER_INTERFACE_IDIOM() macro already includes a runtime check that makes it work on OS 3.0 and 3.1. Since iPhone OS 3.2 is indeed not available on the iPhone, you have to set the Base SDK to 3.2 and the Deployment Target to 3.1 or 3.0 (whatever version you're targeting for the iPhone). That way, all new 3.2 APIs are weakly linked and the app, though compiled against 3.2, will also run on a 3.0 iPhone. And you can use UI_USER_INTERFACE_IDIOM() to do the runtime check. I'm doing this in my universal app that's on the App Store and I can assure you it works.

The only instance where this currently breaks down is if you call class methods on classes that are not available in 3.0/3.1, e.g. UIPopoverController. In a universal app, the compiler will not accept [UIPopoverController alloc] (and as mentioned above, there's no way to wrap this in a conditional compilation block). Instead, you'll have to use [NSClassFromString(@"UIPopoverController") alloc].

Another downside: because we are (weak-)linking against 3.2 now, our app will no longer compile on the 3.1 SDK. And since the iPhone Simulator 3.2 does only work in iPad mode at the moment, the iPhone version of your universal app can only be tested on the device.

Mike said...

I'm not so sure of the accuracy of this. I use user interface idiom in my universal app and it works just fine. The base SDK that it is built against is 3.2 and the deployment target is 3.0. In fact, it was an Apple employee in the dev forum who suggested not using hard coded OS version checks. If I'm not mistaken, UI_INTERFACE_IDIOM will be NULL in OS before 3.2 which will result in false when compared to the uPas identifier.

Alex said...

I use the following macro, which works in OS < 3.2 just fine, and found it very useful when compiling my universal app:
#define IS_IPAD ([[UIDevice currentDevice] respondsToSelector:@selector(userInterfaceIdiom)] && [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)

Jeff LaMarche said...

Ole:

Monday morning posts, you'll have to forgive me for not being very clear. :) You are, of course, right that the UI_USER_INTERFACE_IDIO() does indeed, work for compiling universal binaries. The pre-compiler check allows you to build and run with an Active SDK set to 3.1.2 and run/test on the Simulator.

You can set the active SDK below the Base SDK, so I'm not sure why you can't test the 3.1.3 iPhone build in the simulator of your Universal App. This check actually came from Apple's documentation. Though it's primarily there for creating non-universal apps in a single project, it also works for letting you run on the simulator.

Mike:

Yes, see my response to Ole's comment.

Alex:

That's an interesting macro, however you're still going to get a warning every time you use it if you try and compile with an Active SDK set to 3.1.2.

Ole Begemann said...

Thanks for the clarification, Jeff. Makes sense now.

Jonathan said...

I use
#ifdef UI_USER_INTERFACE_IDIOM() = UIUserInterfaceIdiomPad

I tested it in 3.0 and work fine, the compiler just skipped it if it can't find that define.

John said...

Hi Jeff, Timely post for me so thanks :). One quick thing, I think the preprocessor check needs to be __IPHONE_OS_VERSION_MAX_ALLOWED >= 30200 rather than just >. otherwise version 3.2 will default to the iphone version.

Ole Begemann said...

Sorry to come back to this again, but I've been implementing this in my own code and I believe in the code you posted, Jeff, you should leave the #else out and put the #endif where the #else was instead.

The way the code is now, it will not work correctly on iPhone devices if compiled with OS 4.0 if we assume that 4.0 will be a unified release for iPhone and iPad. On 4.0, the #if condition will be true. Running on an iPhone, the user interface idiom expression will be false so we're in the else branch now. Now NSLog(@"iPhone Idiom"); should be executed but it isn't because the #else block was stripped out by the preprocessor.

So on an iPhone, the line after #endif will be executed. On an iPad, that line would (incorrectly) never be executed, though.

I hope this is clear. I would prefer to show a code block here but formatting it nicely seems to be impossible in a comment (pre and code tags not allowed).

snibbe said...

Reading through this thread, I couldn't find a definitive final statement on the correct code. This worked for me, running correctly in iPhone/iPad simulators and devices:

#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 30200

if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
NSLog(@"iPad Idiom");
}
else

#endif

{
NSLog(@"iPhone Idiom");
}

Neto said...

Same Ole said twelve days ago...
(Just found the problem an hour ago)

Ryan Stubblefield said...

Thanks for the helpful post, and all the useful discussion in the posts.

I am a developer who detests using nibs, and I know I am not alone. So, to serve the needs of others like myself, I created a universal app template that uses the most simple & precisely accurate device detection, and correctly uses the right app delegate, all in code...no nibs.

I've posted the project to the github repository of my iPhone utility lib:

github.com/ryanscott/rcloudlib

It is an extremely lightweight lib that contains the most useful class extensions I've found, as well as a few custom controls and a couple sample app templates. I encourage you to take a look at it...

If you are just interested in a direct download of the universal app template project, use this link:

http://github.com/ryanscott/rcloudlib/raw/1b6cc34f586eaf8206619542ff7a4f89cb991607/Samples/clean_universal_app_template.zip

I will be updating the source of the app template to include my other standard setup, but that .zip file of the clean, simple, untouched version will stay unchanged, so everyone can forever use it as a base to build off of.

If you find any mistakes, or if you have any suggestions, please let me know...I would be happy to discuss incorporating your contributions.

Graeme said...

With regard to using UI_USER_INTERFACE_IDIOM() in the iPhone simulator 3.1.3 I found the following comment regarding using on stackoverflow very helpful:

"... You can build for 3.2 SDK then change the current target to iPhone 3.1 then do either "Run" or "Debug" don't rebuild it. – John Wang" (http://stackoverflow.com/questions/2576356/how-does-one-get-ui-user-interface-idiom-to-work-with-iphone-os-sdk-3-2)

This has worked for me nicely :)

qishaya said...

abercrombiefitch.uk.com propose Concise design model, whether it is Abercrombie fitch or a shirt, jacket is a good array oh.Ultra-Zan’s pants manner. buy Abercrombie Make up your body a little less than the curve.Abercrombie sale Easily with any clothing, different shoes and boots with different styles. Both kind, genial comfort, you can also cool very special Abercrombie .Designed to highlight hurtful the chest, waist and hip, quite close, sexy extraordinary.Companies consider the use of at least 4 time in-gravity Japanese market. Ginza stockroom Abercrombie & FitchAbercrombie Sweaters may be the most costly ever built one of the flagship storeroom, flagship stockpile in cheap Abercrombie compared to superior construction expenses for 1800-2000 million. The circle spokesman said in Tokyo supplies opened for the Abercrombie & Fitch's worldwide growth policy in provisos of a very important measure. Ginza supply is the circle's first mass in Asia, the business campaign to open next year in Fukuoka, Japan, out of a moment Asian restaurant. However, abercrombie stores the spokeswoman did not disclose more shop list.present high level and high assess clothing for youngsters. The food extended of Abercrombie and Fitch embrace not only casual wears,breitling watches, shirts and dresses for the youngsters but it also includes luxury matter such as perfumes and discount Abercrombie accessories. The new limit of yield from Abercrombie and Fitch has superb designs for kids and teenagers. The strain is also recognized for its advertisements,model watches, where you have physically attractivpoorly clad men and women, who begin vigorous, abercrombie outlet enthusiastic, smart and outgoing.Abercrombie 2010

h4ns said...

What youre saying is completely true. I know that everybody must say the same thing, but I just think that you put it in a way that everyone can understand. I also love the images you put in here. They fit so well with what youre trying to say. Im sure youll reach so many people with what youve got to say.

Arsenal vs Huddersfield Town live streaming
Arsenal vs Huddersfield Town live streaming
Wolverhampton Wanderers vs Stoke City Live Streaming
Wolverhampton Wanderers vs Stoke City Live Streaming
Notts County vs Manchester City Live Streaming
Notts County vs Manchester City Live Streaming
Bologna vs AS Roma Live Streaming
Bologna vs AS Roma Live Streaming
Juventus vs Udinese Live Streaming
Juventus vs Udinese Live Streaming
Napoli vs Sampdoria Live Streaming
Napoli vs Sampdoria Live Streaming
Fulham vs Tottenham Hotspur Live Streaming
Fulham vs Tottenham Hotspur Live Streaming
AS Monaco vs Marseille Live Streaming
AS Monaco vs Marseille Live Streaming
Alajuelense vs Perez Zeledon Live Streaming
Alajuelense vs Perez Zeledon Live Streaming
Technology News | News Today | Live Streaming TV Channelsb

AppsTract Technologies said...

I am new to the concept of universal platform for iphone, IPad and IPod, I found this blog really very useful, it answers a lot of the queries I had on this platform
. just wanted to know if there is away in which we can convert an existing iPhone app into a MAC app .....

AppsTract Technologies said...

I am new to the concept of universal platform for iphone, IPad and IPod, I found this blog really very useful, it answers a lot of the queries I had on this platform
. just wanted to know if there is away in which we can convert an existing iPhone app into a MAC app .....

LunaticRaving said...

Just fyi, but instead of a hard coded #, the check should be:


#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_3_2


-lane

healthpharmacyrx1 said...

cialis soft Internet Bestellen
amoxil Online Rezept
minocin Bestellen
zithromax Kaufen
Buy cipro
kamagra Kaufen
cialis Ohne Rezept Bestellen
achat cialis soft france
proscar Bestellen
anafranil Kaufen
lasix Rezept
levitra Prix de Vente
nolvadex Internet Bestellen
cialis Online Rezept
xenical Rezeptfrei Kaufen
cipro Online Kaufen