Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

abcdefg12345

macrumors 6502
Original poster
Jul 10, 2013
281
86
Im trying to support features of an app on OS X Yosemite and later and block them on older versions.

i want my app to run on OS X 10.8 and later and have some features only work on 10.10 and later

will this work to check what operating system is running

Code:
-(void)awakeFromNib{
    if (NSAppKitVersionNumber > NSAppKitVersionNumber10_9) {
        [_sampleobject setHidden:FALSE];
    }else
    {
        [_sampleobject setHidden:TRUE];
    }
}

i want it to hide the object if the application opens on any system earlier than 10.10

and also NSAppKitVersionNumber10_10 docent exist on Xcode 6.1 for some reason
 
To check if a feature is supported you should typically check if the class or method is available rather than checking for the OS version.

Examples below are taken from the SDK Compatibility Guide at:

https://developer.apple.com/library...troduction.html#//apple_ref/doc/uid/10000163i

Code:
if ([UIPrintInteractionController class]) {
              // Create an instance of the class and use it.
          } else {
              // Alternate code path to follow when the
              // class is not available.
}

Code:
if ([UIImagePickerController instancesRespondToSelector:
                        @selector (availableCaptureModesForCameraDevice:)]) {
    // Method is available for use.
    // Your code can check if video capture is available and,
    // if it is, offer that option.
} else {
    // Method is not available.
    // Alternate code to use only still image capture.
}
 
To check if a feature is supported you should typically check if the class or method is available rather than checking for the OS version.

Examples below are taken from the SDK Compatibility Guide at:

https://developer.apple.com/library...troduction.html#//apple_ref/doc/uid/10000163i

Code:
if ([UIPrintInteractionController class]) {
              // Create an instance of the class and use it.
          } else {
              // Alternate code path to follow when the
              // class is not available.
}

Code:
if ([UIImagePickerController instancesRespondToSelector:
                        @selector (availableCaptureModesForCameraDevice:)]) {
    // Method is available for use.
    // Your code can check if video capture is available and,
    // if it is, offer that option.
} else {
    // Method is not available.
    // Alternate code to use only still image capture.
}

Im not trying to check if a feature is supported instead i want the feature to work on 10.10+ only and be hidden on 10.9 and 10.8
 
Im not trying to check if a feature is supported instead i want the feature to work on 10.10+ only and be hidden on 10.9 and 10.8

In that case you will probably want rewrite your check as:

if (NSAppKitVersionNumber > floor(NSAppKitVersionNumber10_9)) {...}

so that that it returns true for 10.10 but false for all 10.9.x versions, in case that NSAppKitVersionNumber10_9_xx are introduced.

The docs I previously linked to list your solution as one of the recommended ways to handle code that should do different things on different OS versions.
 
Im not trying to check if a feature is supported instead i want the feature to work on 10.10+ only and be hidden on 10.9 and 10.8
Usually the reason for doing this is a feature is unsupported in lower OS releases ( e.g. popover windows are 10.7+ )

In Cocoa/Objective-C projects the "respondsToSelector" approach( as mentioned by others ) is my preference. Knowing if a specific call is available for use seems more on point and less likely to suffer from Apple changes.

IMO App-kit version checking has some of the downsides(1) of other version checking and, AFAIK, Apple makes no guarantees to update them or assure they correspond to OS version.

OS version checking code similar to this might work( but I don't like version checking ):

Code:
NSInteger SystemVersionCheck(void) {
   
    NSArray *myarr = [[[NSProcessInfo processInfo] operatingSystemVersionString] componentsSeparatedByString:@" "];
    NSArray *majorMinorBugFix = [myarr[1] componentsSeparatedByString:@"."];
    return [majorMinorBugFix[1] integerValue];   // e.g. major[0] = 10, minor[1] = 9, bugfix[2] = 4. This would return 9.
}

// called with:
if ( SystemVersionCheck() < 10 ) // < 10.10


(1)Stripping the periods '.' from the OS release number can also present a problem: A higher release number such as 1010 being lower(numerically) than a lower release number, 1094, makes comparisons difficult.
 
Last edited:
The [[NSProcessInfo processInfo] operatingSystemVersionString] solution is also rather questionable as the Apple docs point out:

The operating system version string is human readable, localized, and is appropriate for displaying to the user. This string is not appropriate for parsing.

So you can't strictly rely on it using a '.' separator or latin numbers in all countries or languages, Apple could also conceivably change the string format at any time.
 
The [[NSProcessInfo processInfo]
So you can't strictly rely on it using a '.' separator or latin numbers in all countries or languages..

The programmer controls the supported languages, so while this is potentially a problem, the programmer can avoid it by supporting only certain languages.
Apple could also conceivably change the string format at any time.

Agreed. Just as Apple makes no guarantees to match App-Kit version numbers to OS version, they also make no promises about string format. Checking OS version numbers is fraught with pitfalls.
 

The programmer controls the supported languages, so while this is potentially a problem, the programmer can avoid it by supporting only certain languages.

This could (at least theoretically) get rather messy as one may need to consider not just languages but the combination of Language & Region settings. I personally run with English as my preferred language while using Swedish region settings (for time/date/currency/number separators) as I live in Sweden.
This could end up even worse for users that customize their region settings.



Agreed. Just as Apple makes no guarantees to match App-Kit version numbers to OS version, they also make no promises about string format. Checking OS version numbers is fraught with pitfalls.

I would say that they main difference is that the App-kit constants are documented (by Apple) as a way to check for OS versions, while the -operatingSystemVersionString documentation explicitly recommends against using it for that purpose.
The real issue is if one actual needs a minor version number e.g. 10.9.4 vs 10.9.5 - that isn't included among the App-kit constants. This will probably only come up as an issue when OS bugs need to be worked around in specific OS versions.

I kind of miss the old (deprecated) Gestalt solution, which was essentially a better version of -operatingSystemVersionString, see:
http://stackoverflow.com/questions/11072804/how-do-i-determine-the-os-version-at-runtime-in-os-x-or-ios-without-using-gesta
 
what about

Code:
if (rint(NSAppKitVersionNumber) > NSAppKitVersionNumber10_9) {

// OS X 10.10 + features 

}else{

// OS X 10.9 and earlier features

}
 
You can also use

This what I personally use in my - (void)awakeFromNib method.

Code:
if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_8) {
        /* On a 10.8 - 10.8.x system */
        NSLog(@"On a 10.8 - 10.8.x system");
        
    } else if (floor(NSAppKitVersionNumber)<= NSAppKitVersionNumber10_9){
        /* 10.9 - 10.9.X later system */
        NSLog(@"On a 10.9 - 10.9.X system ");

        //So we can received window occlusion state notifications.
        [self.window setDelegate:self];
        
    } else if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_9){
        /* 10.10 */
        NSLog(@"On a 10.10 - 10.10.X system");
//Enable 10.10 only features.

        [self.navigationControls setSegmentStyle:NSSegmentStyleSeparated];
        self.window.titleVisibility = NSWindowTitleHidden;
        self.window.styleMask = self.window.styleMask | NSFullSizeContentViewWindowMask;
        
        
    }
 
what about

Code:
if (rint(NSAppKitVersionNumber) > NSAppKitVersionNumber10_9) {

// OS X 10.10 + features 

}else{

// OS X 10.9 and earlier features

}

This can work :D

But, what I would do since you're only looking for two operating systems is this:

Code:
@interface AppDelegate : NSObject{
    BOOL onYosemite;
}
Then do a initial check:

Code:
if (rint(NSAppKitVersionNumber) > NSAppKitVersionNumber10_9) {

// OS X 10.10 + features 

onYosemite = YES;
}else{

// OS X 10.9 and earlier features

onYosemite = NO;

}

Code:
-(void)something{

if (onYosemite == YES){

//Do things only Yosemite can do

} else{

// Not on Yosemite disable or use alternative method.

}
}
 
Code:
if (rint(NSAppKitVersionNumber) > NSAppKitVersionNumber10_9) {
// OS X 10.10 + features 
}else{
// OS X 10.9 and earlier features
}

Are your sure you want to use rint ?

"The rint() functions return the integral value nearest to x (according to the prevailing rounding mode) in floating-point format."​
- rint man page (on 10.9.5 with Xcode 5.1.1)​

If NSAppKitVersionNumber is 10.10.x this could conceivably round it up to 10.11.0 or down to 10.10.0
 
Code:
if (rint(NSAppKitVersionNumber) > NSAppKitVersionNumber10_9) {
// OS X 10.10 + features 
}else{
// OS X 10.9 and earlier features
}

Are your sure you want to use rint ?

"The rint() functions return the integral value nearest to x (according to the prevailing rounding mode) in floating-point format."​
- rint man page (on 10.9.5 with Xcode 5.1.1)​

If NSAppKitVersionNumber is 10.10.x this could conceivably round it up to 10.11.0 or down to 10.10.0

does that mean 10.9.5 will get rounded to 10.10 instead of 10.9

because with Xcode if you use
Code:
int
it doesn't seem to round the value it just gets rid of the decimal

try this simple code

Code:
- (IBAction)button:(id)sender {
        [_field setIntValue:[_field intValue]];
}

just make a button and a text field and enter numbers with decimals in the text field and try rounding them you'll notice it just gets rid of the decimal and doesn't round the integer of example entering 10.8 gives 10 instead of 11
 
does that mean 10.9.5 will get rounded to 10.10 instead of 10.9

because with Xcode if you use
Code:
int
it doesn't seem to round the value it just gets rid of the decimal

try this simple code

Code:
- (IBAction)button:(id)sender {
        [_field setIntValue:[_field intValue]];
}

just make a button and a text field and enter numbers with decimals in the text field and try rounding them you'll notice it just gets rid of the decimal and doesn't round the integer of example entering 10.8 gives 10 instead of 11


Your mixing together two different issues, how to determine OS version based on NSAppKitVersionNumber as discussed in https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/cross_development/Using/using.html#//apple_ref/doc/uid/20002000-SW6 and how OS UI widgets handle input.

Looking at the NSAppKitVersionNumber... constants (I've listed some of them below), it is fairly obvious that rounding the value of NSAppKitVersionNumber down (using floor) will result in the 10.x.0 OS version.
Also note that the NSAppKitVersionNumber... constants skip some OS version e.g.
10.7.1, 10.7.5, 10.6.1-10.6.8 ...

The Apple docs don't use ceil (or rint) but if NSAppKitVersionNumber was rounded up you would still not get the next major OS version as the 10_6 -> 10_7 -> 10_8 ... increments are larger than +1 so you will probably end up with some kind of non-existent version number.

Code:
#define NSAppKitVersionNumber10_0 577
...
#define NSAppKitVersionNumber10_6 1038
#define NSAppKitVersionNumber10_7 1138
#define NSAppKitVersionNumber10_7_2 1138.23
#define NSAppKitVersionNumber10_7_3 1138.32
#define NSAppKitVersionNumber10_7_4 1138.47
#define NSAppKitVersionNumber10_8 1187
#define NSAppKitVersionNumber10_9 1265

Example for checking for the range 10.7.0-10.7.2, the range 10.7.3-10.8.x, 10.9.x and 10.10.0+ usage.

Code:
if (NSAppKitVersionNumber < NSAppKitVersionNumber10_7) {
  /* On 10.0.0 - 10.6.x system */
} else if (NSAppKitVersionNumber <= NSAppKitVersionNumber10_7_2) {
  /* On a 10.7.0 - 10.7.2 system */
} else if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_8) {
  /* On a 10.7.3 - 10.8.x system */
} else if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_9) {
  /* On a 10.9.0 - 10.9.x system */
} else {
  /* 10.10.0 or later system */
}

In regards to -intValue and -setIntValue: in NSControl (or its subclasses):

  • -setIntValue: takes int as input, so if you pass a float/double/CGFloat/... value, C will simply do its normal type conversion and discard the non-int part.
  • -intValue on the other hand will look at the user input e.g. a text field, and will try to get a int value from it, this may include dropping initial spaces and discarding any content after the first number sequence e.g. " 123abc" will be treated as "123" and will yield the int value of 123. Something like "123.4" will presumably also be treated as "123", but even if it where parsed as a floating point value i.e. as 123.4 it will still end up as 123 when returned as an int, as C will drop the .4 part due to its float -> int type conversion.

Edit: -intValue could certainly be designed to do round/rint before calling return, but this would be a inconsistent behavior considering C type conversion rules for explicit and implicit casts.
 
Last edited:
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.