Support features on OS X 10.10 and later only

Discussion in 'Mac Programming' started by abcdefg12345, Oct 24, 2014.

  1. abcdefg12345 macrumors regular

    abcdefg12345

    Joined:
    Jul 10, 2013
    #1
    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
     
  2. hokan macrumors member

    Joined:
    Mar 18, 2014
    Location:
    Sweden
    #2
    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.
    }
     
  3. abcdefg12345 thread starter macrumors regular

    abcdefg12345

    Joined:
    Jul 10, 2013
    #3
    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
     
  4. hokan macrumors member

    Joined:
    Mar 18, 2014
    Location:
    Sweden
    #4
    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.
     
  5. briloronmacrumo, Oct 26, 2014
    Last edited: Oct 26, 2014

    briloronmacrumo macrumors 6502

    briloronmacrumo

    Joined:
    Jan 25, 2008
    Location:
    USA
    #5
    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.
     
  6. hokan macrumors member

    Joined:
    Mar 18, 2014
    Location:
    Sweden
    #6
    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.
     
  7. briloronmacrumo macrumors 6502

    briloronmacrumo

    Joined:
    Jan 25, 2008
    Location:
    USA
    #7

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

    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.
     
  8. hokan macrumors member

    Joined:
    Mar 18, 2014
    Location:
    Sweden
    #8
    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.


    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
     
  9. Catfish_Man macrumors 68030

    Catfish_Man

    Joined:
    Sep 13, 2001
    Location:
    Portland, OR
    #9
    As of 10.10, NSProcessInfo provides a method -isOperatingSystemAtLeastVersion:
     
  10. abcdefg12345 thread starter macrumors regular

    abcdefg12345

    Joined:
    Jul 10, 2013
    #10
    what about

    Code:
    if (rint(NSAppKitVersionNumber) > NSAppKitVersionNumber10_9) {
    
    // OS X 10.10 + features 
    
    }else{
    
    // OS X 10.9 and earlier features
    
    }
     
  11. DavidBlack macrumors 6502a

    DavidBlack

    Joined:
    Jan 27, 2013
    Location:
    Somewhere In Apple's HQ ;)
    #11
    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;
            
            
        }
    
    
     
  12. DavidBlack macrumors 6502a

    DavidBlack

    Joined:
    Jan 27, 2013
    Location:
    Somewhere In Apple's HQ ;)
    #12
    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.
    
    }
    
    
    }
     
  13. hokan macrumors member

    Joined:
    Mar 18, 2014
    Location:
    Sweden
    #13
    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
     
  14. abcdefg12345 thread starter macrumors regular

    abcdefg12345

    Joined:
    Jul 10, 2013
    #14
    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
     
  15. hokan, Nov 5, 2014
    Last edited: Nov 5, 2014

    hokan macrumors member

    Joined:
    Mar 18, 2014
    Location:
    Sweden
    #15

    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.
     

Share This Page