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

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);
}
61687 次浏览

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.

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");
}

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

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.

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.

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

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.

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.

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.")
}

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];

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!

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 ...

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.

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.

//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