Wanna be 3.x compatible? Not so simple!

iOS 4 icon

So we got the new shiny iOS 4 with the new not-so-shiny SDK 4. Most desirable aspect of using SDK 4 and iOS 4 functions is to be backward compatible with iPhone OS 3.x. This is where you should set your iPhone OS Deployment Target to iPhone OS 3.0 or anything else you want to be compatible with.

Xcode iOS 3 deployment target

This is the official method, and since SDK 4 does NOT come with 3.x headers, the only method to make your app run on 3.x. But it is not so simple, because now in your Xcode you are using iOS 4 API. So how do you know you are not using classes or methods that do not exist in 3.x and putting them into your code will crash your app on 3.x device? You don’t!

Unfortunately Xcode will warn you about deprecated API, but will NOT warn you about API that does not exist on your deployment target. I buy you a beer if you can find such warning option in Xcode project configuration.

This would be acceptable if you could test your application on iPhone 3.x OS Simulator, but there is no any in SDK 4 (neither as an option), hence you can’t absolutely tell your app won’t gonna blow on iPhone OS 3.x if you upgraded your TESTING devices to iOS 4.

I have spent a while trying to figure our what was Apple’s engineers official statement about that on devforums. And they say if you want to test 3.x compatibility you need get a device with iPhone OS 3.x. Huh? So I shall buy an new iPhone expecting it won’t have iOS 4 on board, because there is no (legal) way to downgrade existing device to 3.x.

Trying to find out a more reasonable way I have ended up with installing an OLD SDK 3.2 on /Developer-3.2. Finally tested my application… but fiddling with SDK 4 was not over. (NOTE: Obviously this method isn’t really free as you gonna lost 4GB for old SDK installation and 2.5GB for download, but it far less expensive than getting a new old 3.x device.)

Still there are few gotchas with this method

And here is one of them. If you happen to use iOS4 API in your code, Apple says that your shall check functions (and class) pointers against nil.

One of the new classes introduced by iOS4 API is UITapGestureRecognizer. I happened to know that because 3.x SDK complained about unknown UITapGestureRecognizer class. So I tried in my code:

UITapGestureRecognizer *singleTap = [[UITapGestureRecognizerClass alloc]
    initWithTarget:self action:@selector(handleSingleTap:)];
if( singleTap != nil ) {
    // do something, we shall be there only on iOS4

Should be enough to be compatible with 3.x device compiling on SDK 4, since UITapGestureRecognizer is introduced by iOS4 API I expected that on 3.x it will be nil so singleTap will be nil too! (BTW. It won’t compile with SDK 3.x I have just installed ;P)

I have sent my app to beta tester with 3.x device, that replied the app is still crashing. So my reasoning about nil was NOT true. Okay, fine, let’s try with:

Class TapGestureRecognizerClass = NSClassFromString(@"UITapGestureRecognizer");
if( TapGestureRecognizerClass != nil ) {
    // do something, we shall be there only on iOS4
Again crashes on 3.x device.

I have lost one day on communication with beta tester and scratching my head. You know why? Because UITapGestureRecognizer actually do EXIST in iPhone OS 3.x but it is NOT in the public API. Moreover it misses some methods that do exist in iOS 4 API, so finally I ended up with something like this:

Class TapGestureRecognizerClass = NSClassFromString(@"UITapGestureRecognizer");
if( TapGestureRecognizerClass != nil ) {
    UITapGestureRecognizer *singleTap
        = [[TapGestureRecognizerClass alloc] initWithTarget:self action:@selector(handleSingleTap:)];
    if( singleTap != nil ) {
        if( [singleTap respondsToSelector:@selector(setNumberOfTapsRequired:)] ) {
            [singleTap setNumberOfTapsRequired:1];
        }
        // .. and checking other selectors

And this was my WORKING method to be compatible with 3.x and using some of iOS 4 API. Crazy enough?