3

In a Mac app, how can I programmatically detect (at runtime) whether the app is currently running on a Mac with an Intel or Apple Silicon processor?

Todd Ditchendorf
  • 11,217
  • 14
  • 69
  • 123

3 Answers3

5

In Objective-C, you can use the system uname function. This is equivalent to uname -m from the shell.

#include <sys/utsname.h>

NSString *GetMachineHardwareName(void) {
    struct utsname sysinfo;
    int retVal = uname(&sysinfo);
    if (EXIT_SUCCESS != retVal) return nil;
    
    return [NSString stringWithUTF8String:sysinfo.machine];
}

or in Swift

func GetMachineHardwareName() -> String? {
    var sysInfo = utsname()
    let retVal = uname(&sysInfo)

    guard retVal == EXIT_SUCCESS else { return nil }

    return String(cString: &sysInfo.machine.0, encoding: .utf8)
}

For late-model Intel Macs, this returns x86_64. For Apple Silicon, it returns arm64.

Todd Ditchendorf
  • 11,217
  • 14
  • 69
  • 123
  • How would you do it in Swift, por favor? – El Tomato Oct 19 '21 at 03:36
  • 2
    I edited the answer to include a Swift version that works in Swift 5.5 at least. – Scott Thompson Oct 19 '21 at 04:45
  • Take care that it will give x84_64 as a result for an executable built for x86, non universal binary, eventhough the hardware is apple silicon. One have to use proc_translated to identify that properly. Please check https://stackoverflow.com/questions/66256300/c-c-code-to-have-different-execution-block-on-m1-and-intel/72526494#72526494 (it is C++ version, but it gives the idea) – Titus Jun 07 '22 at 10:54
  • 1
    `String(cString: &sysInfo.machine.0, encoding: .utf8)` will crash with Xcode14 and newer. – Cœur Aug 04 '22 at 08:00
3

Solution compatible with Xcode 14 and newer.

We retrieve utsname.machine and compare it to "arm64":

extension utsname {
    static var sMachine: String {
        var utsname = utsname()
        uname(&utsname)
        return withUnsafePointer(to: &utsname.machine) {
            $0.withMemoryRebound(to: CChar.self, capacity: Int(_SYS_NAMELEN)) {
                String(cString: $0)
            }
        }
    }
    static var isAppleSilicon: Bool {
        sMachine == "arm64"
    }
}
  • "arm64" for Apple Silicon
  • "x86_64" or "i386" for Intel
Cœur
  • 37,241
  • 25
  • 195
  • 267
2

Update For MacOS 12+

Todd's answer will crash when an app is linked against the macOS 12.5 SDK because the sysInfo.machine field is not a null-terminated string and that's apparently now enforced by String(cString:encoding:).

Here's an updated version that will work:

///
///  Determines the architecture of the Mac on which we're running. Returns `arm64` for Apple Silicon
///  and `x86_64` for Intel-based Macs or `nil` if the system call fails.
///
func getMachineHardwareName() -> String?
{
    var sysInfo = utsname()
    let retVal = uname(&sysInfo)
    var finalString: String? = nil
    
    if retVal == EXIT_SUCCESS
    {
        let bytes = Data(bytes: &sysInfo.machine, count: Int(_SYS_NAMELEN))
        finalString = String(data: bytes, encoding: .utf8)
    }
    
    // _SYS_NAMELEN will include a billion null-terminators. Clear those out so string comparisons work as you expect.
    return finalString?.trimmingCharacters(in: CharacterSet(charactersIn: "\0"))
}
Bryan
  • 4,628
  • 3
  • 36
  • 62
  • You should use prefix(while:) to extract everything until the null terminator instead of trimming. Unless its just a couple of null terminators and not a billion. – oxygen Jul 07 '23 at 10:36