How do I determine the OS version at runtime in OS X or iOS (without using Gestalt)?

IosMacosCocoaMacos Carbon

Ios Problem Overview


The Gestalt() function located in CarbonCore/OSUtils.h has been deprecated as of OS X 10.8 Mountain Lion.

I often use this function to test the version of the OS X operating system at runtime (see the toy example below).

What other API could be used to check the OS X operating system version at runtime in a Cocoa application?

int main() {
    SInt32 versMaj, versMin, versBugFix;
    Gestalt(gestaltSystemVersionMajor, &versMaj);
    Gestalt(gestaltSystemVersionMinor, &versMin);
    Gestalt(gestaltSystemVersionBugFix, &versBugFix);

    printf("OS X Version: %d.%d.%d\n", versMaj, versMin, versBugFix);
}

Ios Solutions


Solution 1 - Ios

On OS X 10.10 (and iOS 8.0), you can use [[NSProcessInfo processInfo] operatingSystemVersion] which returns a NSOperatingSystemVersion struct, defined as

typedef struct {
    NSInteger majorVersion;
    NSInteger minorVersion;
    NSInteger patchVersion;
} NSOperatingSystemVersion;

There is also a method in NSProcessInfo that will do the comparison for you:

- (BOOL)isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion)version

Beware, although documented to be available in OS X 10.10 and later, both operatingSystemVersion and isOperatingSystemAtLeastVersion: exist on OS X 10.9 (probably 10.9.2) and work as expected. It means that you must not test if NSProcessInfo responds to these selectors to check if you are running on OS X 10.9 or 10.10.

On iOS, these methods are effectively only available since iOS 8.0.

Solution 2 - Ios

On the command line:

$ sysctl kern.osrelease
kern.osrelease: 12.0.0
$ sysctl kern.osversion
kern.osversion: 12A269

Programmatically:

#include <errno.h>
#include <sys/sysctl.h>

char str[256];
size_t size = sizeof(str);
int ret = sysctlbyname("kern.osrelease", str, &size, NULL, 0);

Darwin version to OS X release:

17.x.x. macOS 10.13.x High Sierra
16.x.x  macOS 10.12.x Sierra
15.x.x  OS X  10.11.x El Capitan
14.x.x  OS X  10.10.x Yosemite
13.x.x  OS X  10.9.x  Mavericks
12.x.x  OS X  10.8.x  Mountain Lion
11.x.x  OS X  10.7.x  Lion
10.x.x  OS X  10.6.x  Snow Leopard
 9.x.x  OS X  10.5.x  Leopard
 8.x.x  OS X  10.4.x  Tiger
 7.x.x  OS X  10.3.x  Panther
 6.x.x  OS X  10.2.x  Jaguar
 5.x    OS X  10.1.x  Puma

A Sample to get and test versions :

#include <string.h>
#include <stdio.h>
#include <sys/sysctl.h>

/* kernel version as major minor component*/
struct kern {
    short int version[3];
};

/* return the kernel version */
void GetKernelVersion(struct kern *k) {
   static short int version_[3] = {0};
   if (!version_[0]) {
      // just in case it fails someday
      version_[0] = version_[1] = version_[2] = -1;
      char str[256] = {0};
      size_t size = sizeof(str);
      int ret = sysctlbyname("kern.osrelease", str, &size, NULL, 0);
      if (ret == 0) sscanf(str, "%hd.%hd.%hd", &version_[0], &version_[1], &version_[2]);
    }
    memcpy(k->version, version_, sizeof(version_));
}

/* compare os version with a specific one
0 is equal
negative value if the installed version is less
positive value if the installed version is more
*/
int CompareKernelVersion(short int major, short int minor, short int component) {
    struct kern k;
    GetKernelVersion(&k);
    if ( k.version[0] !=  major) return major - k.version[0];
    if ( k.version[1] !=  minor) return minor - k.version[1];
    if ( k.version[2] !=  component) return component - k.version[2];
    return 0;
}

int main() {
   struct kern kern;
   GetKernelVersion(&kern);
   printf("%hd %hd %hd\n", kern.version[0], kern.version[1], kern.version[2]);

   printf("up: %d %d eq %d %d low %d %d\n",
        CompareKernelVersion(17, 0, 0), CompareKernelVersion(16, 3, 0),
        CompareKernelVersion(17, 3, 0), CompareKernelVersion(17,3,0),
        CompareKernelVersion(17,5,0), CompareKernelVersion(18,3,0));


}

Result on my machine macOs High Sierra 10.13.2

17 3 0
up: -3 -1 eq 0 0 low 2 1

Solution 3 - Ios

There is the NSAppKitVersionNumber value which you can use to check the various versions of AppKit, although they don't correspond exactly to OS versions

if (NSAppKitVersionNumber <= NSAppKitVersionNumber10_7_2) {
    NSLog (@"We are not running on Mountain Lion");
}

Solution 4 - Ios

There is a cocoa API. You can get an os X version string from the class NSProcessInfo.

The code to get the operating System Version String is below..

NSString * operatingSystemVersionString = [[NSProcessInfo processInfo] operatingSystemVersionString];
    
NSLog(@"operatingSystemVersionString => %@" , operatingSystemVersionString);

// ===>> Version 10.8.2 (Build 12C2034) result value

It isn't deprecated.

Solution 5 - Ios

There is also kCFCoreFoundationVersionNumber which can be used if you only need to check for a minimum version to support. This has the advantage that it works going back to 10.1 and can be done in C, C++, and Objective-C.

For example to check for 10.10 or greater:

#include <CoreFoundation/CoreFoundation.h>
if (floor(kCFCoreFoundationVersionNumber) > kCFCoreFoundationVersionNumber10_9) {
    printf("On 10.10 or greater.");
}

You will need to link with the CoreFoundation (or Foundation) framework.

It also works in Swift in the exact same way. Here's another example:

import Foundation
if floor(kCFCoreFoundationVersionNumber) > kCFCoreFoundationVersionNumber10_8 {
    println("On 10.9 or greater.")
} else if floor(kCFCoreFoundationVersionNumber) > kCFCoreFoundationVersionNumber10_9 {
    println("On 10.10 or greater.")
}

Solution 6 - Ios

You can easily get the major, minor, patch version of the Operating System using NSOperatingSystemVersion

NSOperatingSystemVersion version = [[NSProcessInfo processInfo] operatingSystemVersion];

NSString* major = [NSString stringWithFormat:@"%d", version.majorVersion];


NSString* minor = [NSString stringWithFormat:@"%d", version.minorVersion];


NSString* patch = [NSString stringWithFormat:@"%d", version.patchVersion];

Solution 7 - Ios

Or, to put it more simply, here is the code:

NSDictionary *version = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"];
NSString *productVersion = [version objectForKey:@"ProductVersion"];
NSLog (@"productVersion =========== %@", productVersion);

I hope this helps someone.

Solution 8 - Ios

If you have an app that needs to run on 10.10 as well as prior versions, here's a solution:

typedef struct {
        NSInteger majorVersion;
        NSInteger minorVersion;
        NSInteger patchVersion;
} MyOperatingSystemVersion;
    
if ([[NSProcessInfo processInfo] respondsToSelector:@selector(operatingSystemVersion)]) {
    MyOperatingSystemVersion version = ((MyOperatingSystemVersion(*)(id, SEL))objc_msgSend_stret)([NSProcessInfo processInfo], @selector(operatingSystemVersion));
    // do whatever you want with the version struct here
}
else {
    UInt32 systemVersion = 0;
    OSStatus err = Gestalt(gestaltSystemVersion, (SInt32 *) &systemVersion);
    // do whatever you want with the systemVersion as before
}

Note that even 10.9 seems to respond to the operatingSystemVersion selector, so I think it just was a private API in 10.9 (but still works).

This works on all versions of OS X and doesn't rely on string parsing or file I/O.

Solution 9 - Ios

This is actually a compilation of answers above, with some further guiding to the developer in need.

OS-X provides its version in runtime in several ways. Each way fits better to specific development scenario. I'll try to summarise them all, and hope that others will complete my answer if I forgot something.

First, the comprehensive list of ways to obtain the OS version.

  1. The uname command-line tool and function provides the unix (darwin) version of the OS. Although this is not the marketing version of the OS, it is aligned with it uniquely, so you can deduce the OS-X marketing version from it.
  2. sysctl kern.osrelease command line (or sysctlbyname("kern.osrelease", str, &size, NULL, 0) function) will provide the same information as uname, marginally easier to parse.
  3. Gestalt(gestaltSystemVersionMajor) (with its "Minor" and BugFix" variants is the oldest (pre-Carbon!) API to get the marketing OS version, still supported by long deprecated. Available in C from the CoreServices framework, but not recommended.
  4. NSAppKitVersionNumber is a float constant of the AppKit framework, that will provide the OS-X Appkit version (aligned with the OS version), available to all applications which link against AppKit. It also provides a comprehensive enumeration of all possible versions (e.g. NSAppKitVersionNumber10_7_2)
  5. kCFCoreFoundationVersionNumber is a CoreFoundation framework float constant, identical to the Appkit counterpart, available to all apps linked against CoreFoundation, in both C, Obj-C and Swift. It, too provides a comprehensive enumeration over all OS X released versions (e.g. kCFCoreFoundationVersionNumber10_9)
  6. [[NSProcessInfo processInfo] operatingSystemVersionString]; is a Cocoa API available in Obj-C to both OS-X and iOS applications.
  7. There is a resource .plist in /System/Library/CoreServices/SystemVersion.plist which among other things, contains the OS version in the "ProductVersion" key. NSProcessInfo reads its information from this file, but you can do this directly using your PList-reading API of choice.

For more details on each option - please consult the answers above. There's plenty of information there!

Solution 10 - Ios

There's uname(3):

>The uname() function stores nul-terminated strings of information identifying the current system into the structure referenced by name.

>The utsname structure is defined in the <sys/utsname.h> header file, and contains the following members:

>- sysname - Name of the operating system implementation.

  • nodename - Network name of this machine.
  • release - Release level of the operating system.
  • version - Version level of the operating system.
  • machine - Machine hardware platform.

Solution 11 - Ios

This is what I use:

NSInteger osxVersion;
if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_6) {
    //10.6.x or earlier systems
    osxVersion = 106;
    NSLog(@"Mac OSX Snow Leopard");
} else if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_7) {
    /* On a 10.7 - 10.7.x system */
    osxVersion = 107;
    NSLog(@"Mac OSX Lion");
} else if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_8) {
    /* On a 10.8 - 10.8.x system */
    osxVersion = 108;
    NSLog(@"Mac OSX Moutain Lion");
} else {
    /* 10.9 or later system */
    osxVersion = 109;
    NSLog(@"Mac OSX: Mavericks or Later");
}

It is recommended in AppKit Release Notes

Reading /System/Library/CoreServices/SystemVersion.plist is not possible if the app is sandboxed

Solution 12 - Ios

While playing around with sysctl on macOS today I stumbled upon kern.osproductversion which indeed contains the current OS version. On my Mac running Mojave I get:

$ sysctl kern.osproductversion
kern.osproductversion: 10.14.3

The value can be retrieved programmatically as well, of course:

char str[256];
size_t size = sizeof(str);
int ret = sysctlbyname("kern.osproductversion", str, &size, NULL, 0);

This commit to the xnu kernel code base indicates that osproductversion is a recent addition that came in mid 2018. I successfully tested it on 10.14.3 and 10.13.6.

All in all, the best programmatic, non-Cocoa approach in my opinion would be to check if kern.osproductversion produces a valid result and fall back to kern.osrelease and manual mapping as described in this answer otherwise.

Solution 13 - Ios

Gestalt() is a pure C API. The accepted answer suggests using NSProcessInfo but that requires Objective-C code and cannot be used if pure C is a requirement for some reason. Same holds true for NSAppKitVersionNumber, which also isn't updated anymore by Apple since 10.13.4. Using the constant kCFCoreFoundationVersionNumber was possible in C code but this constant hasn't been updated anymore since 10.11.4. Reading kern.osrelease using sysctl is possible in C but requires a mapping table or relying on the current kernel versioning scheme and thus is not reliable for future version as the kernel version scheme might change and a mapping table cannot know future versions.

The official and recommend way from Apple is to read /System/Library/CoreServices/SystemVersion.plist as this is also where Gestalt() gets the version number from, this is also where NSProcessInfo() gets the version number from, this is also where the command line tool sw_vers gets the version number from and this is even where the new @available(macOS 10.x, ...) compiler feature gets the version number from (I diged deep into the system to find out about this). I wouldn't be surprised if even Swift's #available(macOS 10.x, *) would get the version number from there.

There is just no simple C call for reading the version number from there, so I wrote the following pure C code which only requires CoreFoundation framework that itself is pure C framework:

static bool versionOK;
static unsigned versions[3];
static dispatch_once_t onceToken;

static
void initMacOSVersion ( void * unused ) {
	// `Gestalt()` actually gets the system version from this file.
	// Even `if (@available(macOS 10.x, *))` gets the version from there.
	CFURLRef url = CFURLCreateWithFileSystemPath(
		NULL, CFSTR("/System/Library/CoreServices/SystemVersion.plist"),
		kCFURLPOSIXPathStyle, false);
	if (!url) return;

	CFReadStreamRef readStr = CFReadStreamCreateWithFile(NULL, url);
	CFRelease(url);
	if (!readStr) return;

	if (!CFReadStreamOpen(readStr)) {
		CFRelease(readStr);
		return;
	}

	CFErrorRef outError = NULL;
	CFPropertyListRef propList = CFPropertyListCreateWithStream(
		NULL, readStr, 0, kCFPropertyListImmutable, NULL, &outError);
	CFRelease(readStr);
	if (!propList) {
		CFShow(outError);
		CFRelease(outError);
		return;
	}

	if (CFGetTypeID(propList) != CFDictionaryGetTypeID()) {
		CFRelease(propList);
		return;
	}

	CFDictionaryRef dict = propList;
	CFTypeRef ver = CFDictionaryGetValue(dict, CFSTR("ProductVersion"));
	if (ver) CFRetain(ver);
	CFRelease(dict);
	if (!ver) return;

	if (CFGetTypeID(ver) != CFStringGetTypeID()) {
		CFRelease(ver);
		return;
	}

	CFStringRef verStr = ver;
	// `1 +` for the terminating NUL (\0) character
	CFIndex size = 1 + CFStringGetMaximumSizeForEncoding(
		CFStringGetLength(verStr), kCFStringEncodingASCII);
	// `calloc` initializes the memory with all zero (all \0)
	char * cstr = calloc(1, size);
	if (!cstr) {
		CFRelease(verStr);
		return;
	}

	CFStringGetBytes(ver, CFRangeMake(0, CFStringGetLength(verStr)),
		kCFStringEncodingASCII, '?', false, (UInt8 *)cstr, size, NULL);
	CFRelease(verStr);

	printf("%s\n", cstr);

	int scans = sscanf(cstr, "%u.%u.%u",
		&versions[0], &versions[1], &versions[2]);
	free(cstr);
	// There may only be two values, but only one is definitely wrong.
	// As `version` is `static`, its zero initialized.
	versionOK = (scans >= 2);
}


static
bool macOSVersion (
	unsigned *_Nullable outMajor,
	unsigned *_Nullable outMinor,
	unsigned *_Nullable outBugfix
) {
	dispatch_once_f(&onceToken, NULL, &initMacOSVersion);
	if (versionOK) {
		if (outMajor) *outMajor = versions[0];
		if (outMinor) *outMinor = versions[1];
		if (outBugfix) *outBugfix = versions[2];
	}
	return versionOK;
}

The reason for using a dispatch_once_f instead of dispatch_once is that blocks aren't pure C either. The reason for using dispatch_once at all is that the version number cannot change while the system is running, so there is no reason for reading the version number more than once during a single app run. Also reading it is not guaranteed to be fail-safe and too expensive to re-read it every time your app may require it.

Talking about not being fail-safe, even though it is unlikely, reading it may, of course, fail, in which case the function returns false and always will return false. I leave it up to you to handle that case in a meaningful way in your app code because I cannot know how critical knowing the version number is to your app. If the version number is not critical, maybe you can work around it, otherwise you should make your app fail in a user friendly way.

Note
If you only require the version number to execute alternative code depending on the system version, there might be a better alternative to querying the version number on your own. See this question for details.

Solution 14 - Ios

Late to the game, but I ended up here looking for an answer as well. For what it's worth, maybe it's of use for someone else;

In the past I used the command-line approach:

sw_vers

Which results in:

ProductName:	Mac OS X
ProductVersion:	10.13.6
BuildVersion:	17G65

Each line can be asked for individually (mind the camelback notation):

sw_vers -productVersion
10.13.6

sw_vers -productName
Mac OS X

sw_vers -buildVersion
17G65

Having said that, thanks for all the other solutions listed here ...

Solution 15 - Ios

//my two cents for Swift (Multiplatform)

#if os(iOS)
import UIKit
#elseif os(OSX)
import Cocoa
#endif



func platform() -> String {
    var systemInfo = utsname()
    uname(&systemInfo)
    let size = Int(_SYS_NAMELEN) // is 32, but posix AND its init is 256....
    
    let s = withUnsafeMutablePointer(to: &systemInfo.machine) {p in
        //    let s = withUnsafeMutablePointer(to: &systemInfo.nodename) {p in
        
        p.withMemoryRebound(to: CChar.self, capacity: size, {p2 in
            return String(cString: p2)
        })
        
    }
    return s
}



func AppName()->String{
    let bund = Bundle.main
    if let displayName = bund.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String {
        if displayName.count>0{
            return displayName
        }
    }
    
    if let name = bund.object(forInfoDictionaryKey: "CFBundleName") as? String {
        return name
    }
    return "no AppName"
}


func buildVers()->String{
    
    let bund = Bundle.main
    let vers = bund.object(forInfoDictionaryKey: "CFBundleVersion") as! String
    return  vers
}


func AppleStoreVers()->String{
    
    if let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
        return version
    }
    return "no.vers."
}



#if os(iOS)
func systemVersion()->String{
    let  v = UIDevice.current.systemVersion
    return v
}
#elseif os(OSX)
#endif

Solution 16 - Ios

Update for macOS Catalina and upper

20.x.x  macOS 11.X.X  BigSur
19.x.x  macOS 10.15.x Catalina
18.x.x  macOS 10.14.x Mojave
17.x.x  macOS 10.13.x High Sierra
16.x.x  macOS 10.12.x Sierra
15.x.x  OS X  10.11.x El Capitan
14.x.x  OS X  10.10.x Yosemite
13.x.x  OS X  10.9.x  Mavericks
12.x.x  OS X  10.8.x  Mountain Lion
11.x.x  OS X  10.7.x  Lion
10.x.x  OS X  10.6.x  Snow Leopard
 9.x.x  OS X  10.5.x  Leopard
 8.x.x  OS X  10.4.x  Tiger
 7.x.x  OS X  10.3.x  Panther
 6.x.x  OS X  10.2.x  Jaguar
 5.x    OS X  10.1.x  Puma

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionTodd DitchendorfView Question on Stackoverflow
Solution 1 - Ios0xcedView Answer on Stackoverflow
Solution 2 - IosVariable Length CoderView Answer on Stackoverflow
Solution 3 - IosiainView Answer on Stackoverflow
Solution 4 - IosDHLView Answer on Stackoverflow
Solution 5 - IoskainjowView Answer on Stackoverflow
Solution 6 - IosVikas BansalView Answer on Stackoverflow
Solution 7 - IosneowinstonView Answer on Stackoverflow
Solution 8 - IosSentientAIView Answer on Stackoverflow
Solution 9 - IosMotti ShneorView Answer on Stackoverflow
Solution 10 - IosCarl NorumView Answer on Stackoverflow
Solution 11 - IosTibidaboView Answer on Stackoverflow
Solution 12 - IosbfxView Answer on Stackoverflow
Solution 13 - IosMeckiView Answer on Stackoverflow
Solution 14 - IosHanzaplastiqueView Answer on Stackoverflow
Solution 15 - IosingcontiView Answer on Stackoverflow
Solution 16 - IosvanyabeatView Answer on Stackoverflow