Michael Tyson
Michael Tyson

Reputation: 1488

Can one determine the iOS SDK version used to build a binary, programmatically, at run time?

Ah, iOS 8 - lots of unexpected changes from iOS 7 to account for!

tl;dr: Is there a way to programmatically determine the iOS SDK version used to build an app, at run-time (not with a preprocessor macro)?

I'm struggling with some window frame calculations for a library I maintain (distributed as a pre-built static library), as iOS 8 has changed the way the screen coordinate system works.

Two initial observations, running code for iOS 7 with no changes for iOS 8:

  1. When built with the iOS 7 SDK, and run on iOS 8, everything works as prior, no changes necessary.
  2. When built with the iOS 8 SDK, and run on iOS 8, it's broken: some changes in frame calculation are needed to get correct positioning.

So, we change the code, with conditionals on [[UIDevice currentDevice] systemVersion], to work correctly with the new coordinate system. Now:

  1. When built with the iOS 7 SDK, and run on iOS 7, everything works.
  2. When built with the iOS 8 SDK, and run on iOS 8, everything works.

    BUT:

  3. When built with the iOS 7 SDK, and run on iOS 8, the calculations are off - remember, when built with the iOS 7 SDK, everything worked fine prior to the iOS 8-specific code changes. So, the changes made actually broke stuff.

Now, normally, I could happily solve this with some macro conditionals on the SDK version (#ifdef __IPHONE_8_0, etc). But I'm distributing a pre-built static library, built with the iOS 7 SDK, so the code within those conditionals would never make it in. Here's why that's a problem:

If the static library is built with the iOS 7 SDK, but linked into an app built with the iOS 8 SDK, it's the same as if the static library were built with the iOS 8 SDK (because the linking happens at the final app compilation stage, of course). That means I need to have those iOS 8 changes in there, when the app is built with the iOS 8 SDK -- but I can't use a macro conditional to determine whether to use them, as the C preprocessor did its thing during the static library build under iOS 7.

So, my question is this: does anyone know how I might be able to determine whether the app build was made with the iOS 8 SDK, at runtime, from within the pre-compiled static library?

I did try checking for an iOS 8-only SDK feature (-[UIScreen nativeBounds], for example), but that doesn't fly -- the symbol's available regardless of SDK version.

Anyone have any ideas?

Upvotes: 8

Views: 4690

Answers (2)

Tommy
Tommy

Reputation: 100622

Empirical, undocumented observations follow:

Apple records the SDK you built against in the Info.plist under the keys DTSDKBuild and DTSDKName, amongst others. Of those DTSDKName seems to be accessible at runtime and ends with the SDK number. So, getting a:

- (NSString *)buildVersion
{
    // form character set of digits and punctuation
    NSMutableCharacterSet *characterSet = 
        [[NSCharacterSet decimalDigitCharacterSet] mutableCopy];

    [characterSet formUnionWithCharacterSet:
        [NSCharacterSet punctuationCharacterSet]];

    // get only those things in characterSet from the SDK name
    NSString *SDKName = [[NSBundle mainBundle] infoDictionary][@"DTSDKName"];
    NSArray *components =
        [[SDKName componentsSeparatedByCharactersInSet:
              [characterSet invertedSet]]
                  filteredArrayUsingPredicate:
                      [NSPredicate predicateWithFormat:@"length != 0"]];

    if([components count]) return components[0];
    return nil;
}

BOOL wasBuiltWithiOS8SDK = 
    [[[NSBundle mainBundle] infoDictionary][@"DTSDKBuild"] compare:@"11D167"] 
            == NSOrderedDescending;

... with the heavy caveat that I've just reverse engineered that, empirically. So it's technically undocumented API and there's no guarantee whatsoever of robustness in the future.

You could then just use:

BOOL wasBuiltForiOS8 = 
    [[self buildVersion] compare:@"8.0"] != NSOrderedAscending;

(which has the nice feature that it'll evaluate to YES if the version string isn't found, so technically it doesn't matter if Apple takes away DTSDKBuild in the future, it only matters that they don't retroactively remove it from 7.x or somehow one day use a version string that is alphabetically before 8.0)

Upvotes: 5

Michael Tyson
Michael Tyson

Reputation: 1488

Here's a provisional solution - hacky, though; it'd be nice to have something more robust.

- (BOOL)hasiOS8ScreenCoordinateBehaviour {
    if ( [[[UIDevice currentDevice] systemVersion] floatValue] < 8.0 ) return NO;

    CGSize screenSize = [[UIScreen mainScreen] bounds].size;
    if ( UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation]) &&
            screenSize.width < screenSize.height ) {
        return NO;
    }

    return YES;
}

Upvotes: 3

Related Questions