Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get MAC address from OS X with Swift

Tags:

macos

swift

Is it possible to get the MAC address using Swift?

The MAC address being the primary address for the Wi-Fi or Airport.

I'm trying to make a OS X application.

like image 899
jimbob Avatar asked Aug 05 '15 14:08

jimbob


People also ask

How do I find the MAC address in Swift?

You can not get mac address because of some security concerns. you may use UUID instead to identify device uniquely. UIDevice(). identifierForVendor?.

How do I find my MAC MAC address in terminal?

In the search box, type cmd and press enter. In the command prompt, type getmac and press enter/return. The Physical Address will be your MAC address.

Can Swift be used for macOS apps?

Swift is a robust and intuitive programming language created by Apple for building apps for iOS, Mac, Apple TV, and Apple Watch.


4 Answers

You can also use 'SystemConfiguration' framework

import SystemConfiguration

func collectMACAddresses() -> [String] {
    guard let interfaces = SCNetworkInterfaceCopyAll() as? [SCNetworkInterface] else {
        return []
    }
    
    return interfaces
        .map(SCNetworkInterfaceGetHardwareAddressString)
        .compactMap { $0 as String? }
}
like image 92
Alkenso 'Volodymyr Vashurkin' Avatar answered Sep 28 '22 01:09

Alkenso 'Volodymyr Vashurkin'


Update for Swift 4.2

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! 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, kIOServicePlane, &controllerService) == KERN_SUCCESS {

            let dataUM = IORegistryEntryCreateCFProperty(controllerService, "IOMACAddress" as CFString, kCFAllocatorDefault, 0)
            if dataUM != nil {
                let data = (dataUM!.takeRetainedValue() as! CFData) as Data
                macAddress = [0, 0, 0, 0, 0, 0]
                data.copyBytes(to: &macAddress!, count: macAddress!.count)
            }
            IOObjectRelease(controllerService)
        }

        IOObjectRelease(intfService)
        intfService = IOIteratorNext(intfIterator)
    }

    return macAddress
}


func getMacAddress() -> String? {
    var macAddressAsString : String?
    if let intfIterator = FindEthernetInterfaces() {
        if let macAddress = GetMACAddress(intfIterator) {
            macAddressAsString = macAddress.map( { String(format:"%02x", $0) } ).joined(separator: ":")
            print(macAddressAsString!)
        }

        IOObjectRelease(intfIterator)
    }
    return macAddressAsString
}
like image 37
Marek H Avatar answered Sep 28 '22 02:09

Marek H


Apple's sample code from https://developer.apple.com/library/mac/samplecode/GetPrimaryMACAddress/Introduction/Intro.html to retrieve the Ethernet MAC address can be translated to Swift. I have preserved only the most important comments, more explanations can be found in the original 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 how to work with Unmanaged objects, those have the suffix UM in my code.

Instead of returning an error code, the functions return an optional value which is nil if the function failed.


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)
}
like image 45
Martin R Avatar answered Sep 28 '22 02:09

Martin R


Different approach via 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 {
        print("Error: could not find index for bsd name \(bsd)")
        return nil
    }
    let bsdData = Data(bsd.utf8)
    var managementInfoBase = [CTL_NET, AF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, bsdIndex]

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

    buffer = [CChar](unsafeUninitializedCapacity: length, initializingWith: {buffer, initializedCount in
        for x in 0..<length { buffer[x] = 0 }
        initializedCount = length
    })

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

    let infoData = Data(bytes: buffer, count: length)
    let indexAfterMsghdr = MemoryLayout<if_msghdr>.stride + 1
    let rangeOfToken = infoData[indexAfterMsghdr...].range(of: bsdData)!
    let lower = rangeOfToken.upperBound
    let upper = lower + MAC_ADDRESS_LENGTH
    let macAddressData = infoData[lower..<upper]
    let addressBytes = macAddressData.map{ String(format:"%02x", $0) }
    return addressBytes.joined(separator: separator)
}

MACAddressForBSD(bsd: "en0")
like image 24
vadian Avatar answered Sep 28 '22 00:09

vadian