How to get MAC address from OSX using Swift

Is it possible to get the MAC address using Swift?

The MAC address is the primary address for Wi-Fi or airport.

I am trying to make an OS X application.

+6


source to share


4 answers


DISCLAIMER: This is not production ready. This will likely be rejected by the App Store. It is also error prone if the outcome ifconfig

changes in the future.
I did this because I lacked the skills to translate the C code given in the links. It does not replace Swift's complete solution. That being said, it works ...

Get the ifconfig

output and parse it to get the MAC address associated with the interface ( en0

in this example):



let theTask = NSTask()
let taskOutput = NSPipe()
theTask.launchPath = "/sbin/ifconfig"
theTask.standardOutput = taskOutput
theTask.standardError = taskOutput
theTask.arguments = ["en0"]

theTask.launch()
theTask.waitUntilExit()

let taskData = taskOutput.fileHandleForReading.readDataToEndOfFile()

if let stringResult = NSString(data: taskData, encoding: NSUTF8StringEncoding) {
    if stringResult != "ifconfig: interface en0 does not exist" {
        let f = stringResult.rangeOfString("ether")
        if f.location != NSNotFound {
            let sub = stringResult.substringFromIndex(f.location + f.length)
            let range = Range(start: advance(sub.startIndex, 1), end: advance(sub.startIndex, 18))
            let result = sub.substringWithRange(range)
            println(result)
        }
    }
}

      

+1


source


Apple's sample code from https://developer.apple.com/library/mac/samplecode/GetPrimaryMACAddress/Introduction/Intro.html to get the Ethernet MAC address can be translated into Swift. I have kept only the most important comments, more explanation can be found in the source code.

// Returns an iterator containing the primary (built-in) Ethernet interface. The caller is responsible for
// releasing the iterator after the caller is done with it.
func FindEthernetInterfaces() -> io_iterator_t? {

    let matchingDictUM = IOServiceMatching("IOEthernetInterface");
    // Note that another option here would be:
    // matchingDict = IOBSDMatching("en0");
    // but en0: isn't necessarily the primary interface, especially on systems with multiple Ethernet ports.

    if matchingDictUM == nil {
        return nil
    }
    let matchingDict = matchingDictUM.takeUnretainedValue() as NSMutableDictionary
    matchingDict["IOPropertyMatch"] = [ "IOPrimaryInterface" : true]

    var matchingServices : io_iterator_t = 0
    if IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &matchingServices) != KERN_SUCCESS {
        return nil
    }

    return matchingServices
}

// Given an iterator across a set of Ethernet interfaces, return the MAC address of the last one.
// If no interfaces are found the MAC address is set to an empty string.
// In this sample the iterator should contain just the primary interface.
func GetMACAddress(intfIterator : io_iterator_t) -> [UInt8]? {

    var macAddress : [UInt8]?

    var intfService = IOIteratorNext(intfIterator)
    while intfService != 0 {

        var controllerService : io_object_t = 0
        if IORegistryEntryGetParentEntry(intfService, "IOService", &controllerService) == KERN_SUCCESS {

            let dataUM = IORegistryEntryCreateCFProperty(controllerService, "IOMACAddress", kCFAllocatorDefault, 0)
            if dataUM != nil {
                let data = dataUM.takeRetainedValue() as! NSData
                macAddress = [0, 0, 0, 0, 0, 0]
                data.getBytes(&macAddress!, length: macAddress!.count)
            }
            IOObjectRelease(controllerService)
        }

        IOObjectRelease(intfService)
        intfService = IOIteratorNext(intfIterator)
    }

    return macAddress
}


if let intfIterator = FindEthernetInterfaces() {
    if let macAddress = GetMACAddress(intfIterator) {
        let macAddressAsString = ":".join(macAddress.map( { String(format:"%02x", $0) } ))
        println(macAddressAsString)
    }

    IOObjectRelease(intfIterator)
}

      

The only "tricky" part is working with objects Unmanaged

, they have a suffix UM

in my code.

Instead of returning an error code, functions return an optional value nil

if the function fails.




Update for Swift 3:

func FindEthernetInterfaces() -> io_iterator_t? {

    let matchingDict = IOServiceMatching("IOEthernetInterface") as NSMutableDictionary
    matchingDict["IOPropertyMatch"] = [ "IOPrimaryInterface" : true]

    var matchingServices : io_iterator_t = 0
    if IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &matchingServices) != KERN_SUCCESS {
        return nil
    }

    return matchingServices
}

func GetMACAddress(_ intfIterator : io_iterator_t) -> [UInt8]? {

    var macAddress : [UInt8]?

    var intfService = IOIteratorNext(intfIterator)
    while intfService != 0 {

        var controllerService : io_object_t = 0
        if IORegistryEntryGetParentEntry(intfService, "IOService", &controllerService) == KERN_SUCCESS {

            let dataUM = IORegistryEntryCreateCFProperty(controllerService, "IOMACAddress" as CFString, kCFAllocatorDefault, 0)
            if let data = dataUM?.takeRetainedValue() as? NSData {
                macAddress = [0, 0, 0, 0, 0, 0]
                data.getBytes(&macAddress!, length: macAddress!.count)
            }
            IOObjectRelease(controllerService)
        }

        IOObjectRelease(intfService)
        intfService = IOIteratorNext(intfIterator)
    }

    return macAddress
}

if let intfIterator = FindEthernetInterfaces() {
    if let macAddress = GetMACAddress(intfIterator) {
        let macAddressAsString = macAddress.map( { String(format:"%02x", $0) } )
            .joined(separator: ":")
        print(macAddressAsString)
    }

    IOObjectRelease(intfIterator)
}

      

+13


source


Different approach through if_msghdr

func MACAddressForBSD(bsd : String) -> String?
{
  let MAC_ADDRESS_LENGTH = 6
  let separator = ":"

  var length : size_t = 0
  var buffer : [CChar]

  let BSDIndex = Int32(if_nametoindex(bsd))
  if BSDIndex == 0 {
    println("Error: could not find index for bsd name \(bsd)")
    return nil
  }
  let bsdData = bsd.dataUsingEncoding(NSUTF8StringEncoding)!

  var managementInfoBase = [CTL_NET, AF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, BSDIndex]

  if sysctl(&managementInfoBase, 6, nil, &length, nil, 0) < 0 {
    println("Error: could not determine length of info data structure");
    return nil;
  }

  buffer = [CChar](count: length, repeatedValue: 0)

  if sysctl(&managementInfoBase, 6, &buffer, &length, nil, 0) < 0 {
    println("Error: could not read info data structure");
    return nil;
  }

  let infoData = NSData(bytes: buffer, length: length)
  var interfaceMsgStruct = if_msghdr()
  infoData.getBytes(&interfaceMsgStruct, length: sizeof(if_msghdr))
  let socketStructStart = sizeof(if_msghdr) + 1
  let socketStructData = infoData.subdataWithRange(NSMakeRange(socketStructStart, length - socketStructStart))
  let rangeOfToken = socketStructData.rangeOfData(bsdData, options: NSDataSearchOptions(0), range: NSMakeRange(0, socketStructData.length))
  let macAddressData = socketStructData.subdataWithRange(NSMakeRange(rangeOfToken.location + 3, MAC_ADDRESS_LENGTH))
  var macAddressDataBytes = [UInt8](count: MAC_ADDRESS_LENGTH, repeatedValue: 0)
  macAddressData.getBytes(&macAddressDataBytes, length: MAC_ADDRESS_LENGTH)
  let addressBytes = macAddressDataBytes.map({ String(format:"%02x", $0) })

  return join(separator, addressBytes)
}


MACAddressForBSD("en0")

      

+4


source


Update Martin R. There are a couple of lines that won't compile with Swift 2.1.

Edit:

let matchingDict = matchingDictUM.takeUnretainedValue() as NSMutableDictionary

      

To:

let matchingDict = matchingDictUM as NSMutableDictionary

      

Edit:

let macAddressAsString = ":".join(macAddress.map( { String(format:"%02x", $0) } ))

      

To:

let macAddressAsString = macAddress.map( { String(format:"%02x", $0) } ).joinWithSeparator(":")

      

+3


source







All Articles