Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are the common use cases for __IPHONE_OS_VERSION_MAX_ALLOWED?

What are the situations in which you would use the __IPHONE_OS_VERSION_MAX_ALLOWED check? What about __IPHONE_OS_VERSION_MIN_REQUIRED?

like image 874
Boon Avatar asked Sep 24 '11 22:09

Boon


2 Answers

It's important to understand that these are compile-time constants, they are therefore not useful for detecting at runtime what platform or OS version you are running on (e.g. detecting if you are running on iPad vs iPhone).

What these constants do is allow you to detect at compile time whether the code is being built for a given SDK or deployment target. For example, if you wrote an open source library that contains code that only works when compiled against the iOS 5 SDK, you might include this check to detect which SDK the code is being compiled for:

#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 50000
    //you can use iOS 5 APIs here because the SDK supports them
    //but the code may still crash if run on an iOS 4 device
#else
    //this code can't use iOS 5 APIs as the SDK version doesn't support them
#endif

Or alternatively, if you want to see what the minimum OS version being targeted is...

#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 50000
    //minimum deployment target is 5.0, so it's safe to use iOS 5-only code
#else
    //you can use iOS5 APIs, but the code will need to be backwards
    //compatible or it will crash when run on an iOS 4 device
#endif

This is different from detecting at runtime what OS you are running on. If you compile the code in the first example above using the iOS 4 SDK it will use your iOS 4-safe code, but won't take advantage of any iOS 5 features when run on an iOS 5 device. If you build it using the iOS 5 SDK then set the deployment target to iOS 4 and try to run it on an iOS 4 device, it will compile and install fine but may still crash at runtime because the iOS 5 APIs aren't there.

In the second example above, if you set your deployment target to iOS 4 or below then it will use the iOS 4-safe code path, but if you set the deployment target to iOS 5, it won't run at all on an iOS 4 device (it will refuse to install).

To build an app that runs on iOS 4 and 5 and is still able to take advantage of iOS 5 features if they are available, you need to do run-time detection. To detect the iOS version at runtime you can do this:

if ([[[UIDevice currentDevice] systemVersion] compare:@"5.0.1" options:NSNumericSearch] != NSOrderedAscending) {
    //running on iOS 5.0.1 or higher
}

But that means keeping track of exactly which API features were added in which OS version, which is clunky and should only be done as a last resort. Usually, a better approach is to use feature detection, like this:

if ([SomeClass class]) {
    //this class exists
}

if ([SomeClass instancesRespondToSelector:@selector(someMethod:)]) {
    //this method exists
}

Also, to detect at runtime if you are on an iPad or iPhone, you can do this:

if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
    //on an ipad
}

Performing these checks at runtime allows you to create a single app that runs on multiple devices and iOS versions and is able to take advantage of the features of each platform.

like image 108
Nick Lockwood Avatar answered Nov 20 '22 14:11

Nick Lockwood


a practical implementation/example of using instancesRespondToSelector:, expanding on @Nick Lockwood's anwser:

+(BOOL) testIsHeaderInConnectData:(NSData *) connectData {
    static NSString *headString = nil;
    static NSData *headData = nil;
    static BOOL rangeCheckOk = NO;
    static BOOL rangeCheckTestComplete = NO;
    if (!rangeCheckTestComplete) {
        rangeCheckOk = [NSData instancesRespondToSelector:@selector(rangeOfData:options:range:)]; 
        headString = @"HEAD ";
        headData = (rangeCheckOk?[[NSData alloc] initWithBytes:headString.UTF8String length:headString.length]:nil);
        headString = (rangeCheckOk?nil:[[NSString alloc ]initWithString:headString]);
        rangeCheckTestComplete = YES;
    }

    NSInteger testLength = connectData.length;
    BOOL isHeader = testLength > 5;
    if (isHeader) {
        testLength = (testLength < 128?testLength:128);
        if (rangeCheckOk) {
             isHeader = [connectData rangeOfData:headData options:0 range:(NSRange){0,testLength}].location!=NSNotFound;
        } else {
            NSString *headStart = [[NSString alloc] initWithBytes:connectData.bytes length:testLength encoding:NSUTF8StringEncoding];
            isHeader = [headStart rangeOfString:headString].location!=NSNotFound;
            [headStart release];
        }
    }
    return isHeader;
}
like image 29
unsynchronized Avatar answered Nov 20 '22 16:11

unsynchronized