I have an iOS app and a MacOS app in which I want to display to the user it's device's local IP.
If there is more than one IP, I would dispaly one of them, not matter which one.
This is the code I'm using:
func getIFAddresses() -> String {
//var addresses = [String]()
var address = "N/A"
deviceLocalIp = "N/A"
// Get list of all interfaces on the local machine:
var ifaddr : UnsafeMutablePointer?
guard getifaddrs(&ifaddr) == 0 else { return address }
guard let firstAddr = ifaddr else { return address }
// For each interface ...
for ptr in sequence(first: firstAddr, next: { $0.pointee.ifa_next }) {
let flags = Int32(ptr.pointee.ifa_flags)
var addr = ptr.pointee.ifa_addr.pointee
// Check for running IPv4, IPv6 interfaces. Skip the loopback interface.
if (flags & (IFF_UP|IFF_RUNNING|IFF_LOOPBACK)) == (IFF_UP|IFF_RUNNING) {
if addr.sa_family == UInt8(AF_INET) || addr.sa_family == UInt8(AF_INET6) {
let interfaceName = String.init(cString: &ptr.pointee.ifa_name.pointee)
//DDLogInfo("interfaceName:\(interfaceName)")
// Convert interface address to a human readable string:
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
if (getnameinfo(&addr, socklen_t(addr.sa_len), &hostname, socklen_t(hostname.count),
nil, socklen_t(0), NI_NUMERICHOST) == 0) {
if interfaceName == "en0" {
deviceLocalIp = String(cString: hostname)
address = deviceLocalIp
break
}
//if we don't have address from en0 - try get it from another interface
//(but prefer from en0)
if address == "N/A" && (interfaceName == "en0" || interfaceName == "en1" || interfaceName == "en2" || interfaceName == "pdp_ip" || interfaceName == "ap1") {
deviceLocalIp = String(cString: hostname)
address = deviceLocalIp
}
}
}
}
}
freeifaddrs(ifaddr)
return address
}
}
For IPv4 it seems to work well.
For IPv6 (via Mac's Internet Sharing), I'm getting an IPv6 address, but it's not the address I'm expecting to connect -
at the Network I see that my device is connected and has the IP address X and the result I'm getting with this code is address Y.
P.S -
For debugging, I printed all the IPs, not just the first, and still didn't get the correct one..
Your code has a bug that causes it to fail with large (and hence IPv6) addresses. Specifically, in line 14 you assign
ptr.pointee.ifa_addr.pointee
to
addr
, but
ptr.pointee.ifa_addr.pointee
is of type
sockaddr
, which isn’t big enough to hold an IPv6 address. What you should be doing is assigning
addr
to
ptr.pointee.ifa_addr
, that is, an
UnsafeMutablePointer<sockaddr>
, and that passing that directly to
getnameinfo
.
Pasted in below is a function that prints the address for each interface.
Share and Enjoy
—
Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
let myEmail = "eskimo" + "1" + "@apple.com"
func printAddresses() {
var addrList : UnsafeMutablePointer<ifaddrs>?
guard
getifaddrs(&addrList) == 0,
let firstAddr = addrList
else { return }
defer { freeifaddrs(addrList) }
for cursor in sequence(first: firstAddr, next: { $0.pointee.ifa_next }) {
let interfaceName = String(cString: cursor.pointee.ifa_name)
let addrStr: String
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
if
let addr = cursor.pointee.ifa_addr,
getnameinfo(addr, socklen_t(addr.pointee.sa_len), &hostname, socklen_t(hostname.count), nil, socklen_t(0), NI_NUMERICHOST) == 0,
hostname[0] != 0
{
addrStr = String(cString: hostname)
} else {
addrStr = "?"
}
print(interfaceName, addrStr)
}
return
}