Skip to content

Instantly share code, notes, and snippets.

@macshome
Last active February 18, 2026 06:26
Show Gist options
  • Select an option

  • Save macshome/294f4ca723c50d2ed94773e5873bce4b to your computer and use it in GitHub Desktop.

Select an option

Save macshome/294f4ca723c50d2ed94773e5873bce4b to your computer and use it in GitHub Desktop.
This playground shows you a few different ways to get device info via IOKit.
// This playground shows you a few different ways to get device info via IOKit.
// If you want to explore IOKit and look for interesting data I would download
// the Additional Developer Tools from Apple and use the IORegistryExplorer app.
// It makes it super easy to poke around in the IOKit planes.
import IOKit
import Foundation
// For convient access we can make a computed getter for the PlatformExpert.
// Traditionally this has been where you go to find all sorts of data about the
// Mac. With Apple Silicon though there seems to be a shift to store product
// data over in a new service.
//
// The class inheritance of this object is: IOPlatformExpertDevice : IOService : IORegistryEntry : OSObject
//
// We will cover that below as well.
private var platformExpert: io_service_t {
IOServiceGetMatchingService(kIOMainPortDefault, IOServiceMatching("IOPlatformExpertDevice"))
}
// This domain is just to show you how to find a different one. In this case it's the power management
// domain where you can find things like Clamshell State.
private var pmRootDomain : io_service_t {
IOServiceGetMatchingService(kIOMainPortDefault, IOServiceMatching("IOPMrootDomain"))
}
// This method will take a IOKit key as a String and then return a string if it finds a value
// in the sepcified IOKit domain.
// The types in these examples are just CFBoolean, CFString or CFData on my Mac, so that's all I handle
// in this example.
//
// If you are new to working with CFTypes they aren't that scary. A lot of methods
// will return a clear type like CFString or CFData and you can just cast those in
// most cases. Some methods, like the one we are using here, return an opaque CFRef.
// The simplest way to convert them cleanly to Swift is to check and see what the type
// of data held in the CFRef is with CFGetTypeID() and then compare that to known
// CFTypes.
//
// For debugging you can just dump a CFRef to the console with CFShow().
//
// Make sure you take the retained value of the IOKit container. That way ARC knows to release it.
func getIOPlatformString(_ key: String, domain: io_service_t) -> String? {
let iokValue = IORegistryEntryCreateCFProperty(domain,
(key as CFString),
kCFAllocatorDefault,
0).takeRetainedValue()
let typeID = CFGetTypeID(iokValue)
if typeID == CFStringGetTypeID(),
let iokString = iokValue as? String {
return iokString
}
if typeID == CFBooleanGetTypeID(),
let iokBool = iokValue as? Bool {
return String(iokBool)
}
if typeID == CFDataGetTypeID(),
let data = iokValue as? Data,
let iokString = String(data: data, encoding: .utf8)?.trimmingCharacters(in: .newlines) {
return iokString
}
return nil
}
// On Apple Silicon there seems to be a move to use another area of the IOService plane, AppleARMPE.
// Inside there is a "product" class that contains the same basic info as the PLatformExpert
// but it also has more info as well. Notice that I can also create and release the IOKit Object
// right after using it in my method as well.
//
// The class inheritance of this object is: IOPlatformDevice : IOService : IORegistryEntry : OSObject
//
// Just to show something different we get our registry entry here in a different way.
// If we use a mach_port to search from then we can just sepcify the path to the entry that
// we want a snapshot of. After that we can convert the CFType to a String and print it out.
//
// Note that I'm assuming Data as the returned value type here as that's the only type I've seen in this
// registry entry. In real code you should check it.
func getProductDescriptionARM() -> String? {
let appleSiliconProduct = IORegistryEntryFromPath(kIOMainPortDefault, "IOService:/AppleARMPE/product")
let cfKeyValue = IORegistryEntryCreateCFProperty(appleSiliconProduct,
"product-description" as CFString,
kCFAllocatorDefault,
0)
IOObjectRelease(appleSiliconProduct)
if let data = cfKeyValue?.takeRetainedValue() as? Data {
return String(data: data, encoding: .utf8)?.trimmingCharacters(in: .newlines)
}
return nil
}
print("This Mac's UUID from PExpert is: \(getIOPlatformString("IOPlatformUUID", domain: platformExpert) ?? "Not found.")")
print("This Mac's model from PExpert is: \(getIOPlatformString("model", domain: platformExpert) ?? "Not found.")")
print("This Mac's clamshell state from PMRoot is: \(getIOPlatformString("AppleClamshellState", domain: pmRootDomain) ?? "Not found.")")
print("This Mac's product description from AppleARMPE is: \(getProductDescriptionARM() ?? "Not found.")")
@macshome
Copy link
Author

macshome commented Apr 24, 2024

Sample output:

This Mac's UUID from PExpert is: D6B025BC-4F98-5242-9F33-38065114CC60
This Mac's model from PExpert is: MacBookPro18,1
This Mac's clamshell state from PMRoot is: false
This Mac's product description from AppleARMPE is: MacBook Pro (16-inch, 2021)

@macshome
Copy link
Author

Fixed some ugly code for making Strings.

@xhruso00
Copy link

The code has memory leaks. Need to use takeRetainedValue

@macshome
Copy link
Author

macshome commented Apr 29, 2024

The code has memory leaks. Need to use takeRetainedValue

I wasn't super worried about it as it's a playground, but you are correct. I've updated it so that it doesn't catch anyone by surprise.

@charlieMonroe
Copy link

You should use trimmingCharacters(in: .newlines.union(.controlCharacters)) when initializing the string from data - otherwise it results in a string that has a \0 at the end. Might not be an issue for printing it to console or in a playground, but if you e.g. encode it in JSON, it might be an issue. Alternatively, you'd need to use String initializers that take C-string which trims the terminating character.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment