Swift - 获取设备的 WIFI IP 地址

Posted

技术标签:

【中文标题】Swift - 获取设备的 WIFI IP 地址【英文标题】:Swift - Get device's WIFI IP Address 【发布时间】:2015-08-25 05:47:36 【问题描述】:

我需要在 Swift 中获取 ios 设备的 IP 地址。这与其他有关此问题的问题不重复!如果没有 wifi IP 地址,我只需要获取 WiFi IP 地址 - 我需要处理它。 Stack Overflow 上有几个关于它的问题,但只有返回 ip 地址的函数。例如(来自How to get Ip address in swift):

func getIFAddresses() -> [String] 
    var addresses = [String]()

    // Get list of all interfaces on the local machine:
    var ifaddr : UnsafeMutablePointer<ifaddrs> = nil
    if getifaddrs(&ifaddr) == 0 

        // For each interface ...
        for (var ptr = ifaddr; ptr != nil; ptr = ptr.memory.ifa_next) 
            let flags = Int32(ptr.memory.ifa_flags)
            var addr = ptr.memory.ifa_addr.memory

            // 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) 

                    // Convert interface address to a human readable string:
                    var hostname = [CChar](count: Int(NI_MAXHOST), repeatedValue: 0)
                    if (getnameinfo(&addr, socklen_t(addr.sa_len), &hostname, socklen_t(hostname.count),
                        nil, socklen_t(0), NI_NUMERICHOST) == 0) 
                            if let address = String.fromCString(hostname) 
                                addresses.append(address)
                            
                    
                
            
        
        freeifaddrs(ifaddr)
    

    return addresses

在这里我得到 2 个值 - 来自移动互联网的地址(我认为)和我需要的 WiFi 地址。有没有其他方法可以只获取 WiFi IP 地址?

【问题讨论】:

【参考方案1】:

创建桥接头并在其中包含#include &lt;ifaddrs.h&gt;

那就写这个方法

func getIFAddresses() -> [String] 
var addresses = [String]()

// Get list of all interfaces on the local machine:
var ifaddr : UnsafeMutablePointer<ifaddrs> = nil
if getifaddrs(&ifaddr) == 0 

    // For each interface ...
    for (var ptr = ifaddr; ptr != nil; ptr = ptr.memory.ifa_next) 
        let flags = Int32(ptr.memory.ifa_flags)
        var addr = ptr.memory.ifa_addr.memory

        // 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) 

                // Convert interface address to a human readable string:
                var hostname = [CChar](count: Int(NI_MAXHOST), repeatedValue: 0)
                if (getnameinfo(&addr, socklen_t(addr.sa_len), &hostname, socklen_t(hostname.count),
                    nil, socklen_t(0), NI_NUMERICHOST) == 0) 
                        if let address = String.fromCString(hostname) 
                            addresses.append(address)
                        
                
            
        
    
    freeifaddrs(ifaddr)


  return addresses

当我像 var arr : NSArray = self.getIFAddresses() 这样在我的 viewController 中调用这个方法时,我会在我的控制台中得到完美的响应,比如

IP :( “10.0.0.94” )

从这个数组你可以在任何你想要的地方访问它。 希望对你有帮助

【讨论】:

它返回数组f地址,如何从那里获取wifi地址? 你可以轻松地从数组中访问你想要的......不是吗? "then write this method ..." –这就是OP在问题中所写的,您的代码是精确副本(也与此处发布的代码相同: ***.com/a/25627545/1187415)。 嗨。我在尝试 UInt8(...) 方法时崩溃了,有人可以帮忙吗? ***.com/questions/55491986/…【参考方案2】:

根据几个 SO 线程(例如What exactly means iOS networking interface name? what's pdp_ip ? what's ap?),iOS 设备上的 WiFi 接口总是名称为“en0”。

您的代码(这似乎是我在How to get Ip address in swift 回答的内容:)检索所有 正在运行的网络接口的IP 地址列表。它可以很容易地修改为只返回 IP 地址 “en0”接口,实际上这就是我最初所拥有的 在那个线程上回答(这只是对 回复how to get ip address of iphone programmatically):

// Return IP address of WiFi interface (en0) as a String, or `nil`
func getWiFiAddress() -> String? 
    var address : String?

    // Get list of all interfaces on the local machine:
    var ifaddr : UnsafeMutablePointer<ifaddrs> = nil
    if getifaddrs(&ifaddr) == 0 

        // For each interface ...
        var ptr = ifaddr
        while ptr != nil 
            defer  ptr = ptr.memory.ifa_next 

            let interface = ptr.memory

            // Check for IPv4 or IPv6 interface:
            let addrFamily = interface.ifa_addr.memory.sa_family
            if addrFamily == UInt8(AF_INET) || addrFamily == UInt8(AF_INET6) 

                // Check interface name:
                if let name = String.fromCString(interface.ifa_name) where name == "en0" 

                    // Convert interface address to a human readable string:
                    var hostname = [CChar](count: Int(NI_MAXHOST), repeatedValue: 0)
                    getnameinfo(interface.ifa_addr, socklen_t(interface.ifa_addr.memory.sa_len),
                                &hostname, socklen_t(hostname.count),
                                nil, socklen_t(0), NI_NUMERICHOST)
                    address = String.fromCString(hostname)
                
            
        
        freeifaddrs(ifaddr)
    

    return address

用法:

if let addr = getWiFiAddress() 
    print(addr)
 else 
    print("No WiFi address")


Swift 3 更新:除了采用代码 many changes in Swift 3, 遍历所有接口现在可以使用新的泛化 sequence()函数:

不要忘记在您的桥接头中添加#include &lt;ifaddrs.h&gt;

// Return IP address of WiFi interface (en0) as a String, or `nil`
func getWiFiAddress() -> String? 
    var address : String?

    // Get list of all interfaces on the local machine:
    var ifaddr : UnsafeMutablePointer<ifaddrs>?
    guard getifaddrs(&ifaddr) == 0 else  return nil 
    guard let firstAddr = ifaddr else  return nil 

    // For each interface ...
    for ifptr in sequence(first: firstAddr, next:  $0.pointee.ifa_next ) 
        let interface = ifptr.pointee

        // Check for IPv4 or IPv6 interface:
        let addrFamily = interface.ifa_addr.pointee.sa_family
        if addrFamily == UInt8(AF_INET) || addrFamily == UInt8(AF_INET6) 

            // Check interface name:
            let name = String(cString: interface.ifa_name)
            if  name == "en0" 

                // Convert interface address to a human readable string:
                var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
                getnameinfo(interface.ifa_addr, socklen_t(interface.ifa_addr.pointee.sa_len),
                            &hostname, socklen_t(hostname.count),
                            nil, socklen_t(0), NI_NUMERICHOST)
                address = String(cString: hostname)
            
        
    
    freeifaddrs(ifaddr)

    return address


对于那些来寻找比 WIFI IP 更多的人,您可以稍微修改此代码

func getAddress(for network: Network) -> String? 
    var address: String?

    // Get list of all interfaces on the local machine:
    var ifaddr: UnsafeMutablePointer<ifaddrs>?
    guard getifaddrs(&ifaddr) == 0 else  return nil 
    guard let firstAddr = ifaddr else  return nil 

    // For each interface ...
    for ifptr in sequence(first: firstAddr, next:  $0.pointee.ifa_next ) 
        let interface = ifptr.pointee

        // Check for IPv4 or IPv6 interface:
        let addrFamily = interface.ifa_addr.pointee.sa_family
        if addrFamily == UInt8(AF_INET) || addrFamily == UInt8(AF_INET6) 

            // Check interface name:
            let name = String(cString: interface.ifa_name)
            if name == network.rawValue 

                // Convert interface address to a human readable string:
                var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
                getnameinfo(interface.ifa_addr, socklen_t(interface.ifa_addr.pointee.sa_len),
                            &hostname, socklen_t(hostname.count),
                            nil, socklen_t(0), NI_NUMERICHOST)
                address = String(cString: hostname)
            
        
    
    freeifaddrs(ifaddr)

    return address

enum Network: String 
    case wifi = "en0"
    case cellular = "pdp_ip0"
    //... case ipv4 = "ipv4"
    //... case ipv6 = "ipv6"

那么我们也可以访问蜂窝 IP。

guard let wifiIp = getAddress(for: .wifi) else return

&

guard let cellularIp = getAddress(for: .cellular) else return

【讨论】:

@Martin R,C 风格的 for 语句已弃用,将在 Swift 的未来版本中删除。你能在你的答案中解决它吗? @MartinR 非常感谢,伙计。 :) @MartinR 我创建了一个桥接头文件,然后在该文件中包含 #include ,并将您的 swift3 代码粘贴到我的视图控制器中,但是 getifaddrs() 、 ifaddrs 、 freeifaddrs 有错误,怎么解决? @JayprakashDubey:此代码专用于 WiFi(“en0”)地址。请参阅 ***.com/a/25627545/1187415 获取检索所有正在运行的接口的 IP 地址的代码(并且在我的测试中与蜂窝网络一起使用)。 @MegaManX: fe80::10ec:6bd9:1d19:ac29%en0 是"scoped literal IPv6 address“【参考方案3】:

您可以使用以下代码获取 IP 地址:

注意:我使用了可达性,以便在 WiFi 更改为另一个时捕获新的 IP 地址。

    Podfile 文件中

    pod 'ReachabilitySwift' 然后install pod

    AppDelegate.swift 文件中import ReachabilitySwift

    注意:如果提示找不到 ReachabilitySwift 模块的错误,则只需复制并粘贴即可。有效!

    didFinishLaunchingOptions函数

    NotificationCenter.default.addObserver(self, selector: #selector(self.reachabilityChanged), name: ReachabilityChangedNotification, object: reachability)
    
    do
        try reachability.startNotifier()
    
    catch 
        print("could not start reachability notifier")
     
    

    然后将下面的代码复制粘贴到AppDelegate文件中

    func reachabilityChanged(note: NSNotification) 
    
        let reachability = note.object as! Reachability
    
        if reachability.isReachable 
            if reachability.isReachableViaWiFi 
                print("Reachable via WiFi")
             else 
                print("Reachable via Cellular")
            
    
            setIPAddress()
         else 
            ipAddress = "" // No IP captures
            print("Network not reachable")
        
    
    
    func setIPAddress() 
        if let addr = self.getWiFiAddress() 
            print("ipAddress : \(addr)")
            ipAddress = addr
         else 
            ipAddress = "" // No IP captures
            print("No WiFi address")
        
    
    
    // Return IP address of WiFi interface (en0) as a String, or `nil`
    func getWiFiAddress() -> String? 
        var address : String?
    
        // Get list of all interfaces on the local machine:
        var ifaddr : UnsafeMutablePointer<ifaddrs>?
        guard getifaddrs(&ifaddr) == 0 else  return nil 
        guard let firstAddr = ifaddr else  return nil 
    
        // For each interface ...
        for ifptr in sequence(first: firstAddr, next:  $0.pointee.ifa_next ) 
            let interface = ifptr.pointee
    
            // Check for IPv4 or IPv6 interface:
            let addrFamily = interface.ifa_addr.pointee.sa_family
            if addrFamily == UInt8(AF_INET) || addrFamily == UInt8(AF_INET6) 
    
                // Check interface name:
                let name = String(cString: interface.ifa_name)
                if  name == "en0" 
    
                    // Convert interface address to a human readable string:
                    var addr = interface.ifa_addr.pointee
                    var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
                    getnameinfo(&addr, socklen_t(interface.ifa_addr.pointee.sa_len),
                                &hostname, socklen_t(hostname.count),
                                nil, socklen_t(0), NI_NUMERICHOST)
                    address = String(cString: hostname)
                
            
        
        freeifaddrs(ifaddr)
    
        return address
    
    

    将此添加到桥接头文件#include&lt;ifaddrs.h&gt;

    如果你没有这个文件,那么你可以创建它Check this link

6.

func applicationWillEnterForeground(_ application: UIApplication) 
        // Post notification
        NotificationCenter.default.post(name: ReachabilityChangedNotification, object: reachability)
        // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
    

    如果你想移除观察者,那么:

    reachability.stopNotifier()  
    
    NSNotificationCenter.defaultCenter().removeObserver(self,name: ReachabilityChangedNotification,object: reachability)
    

【讨论】:

【参考方案4】:
func getIPAddress() -> String 
    var address: String = "error"

    var interfaces: ifaddrs? = nil

    var temp_addr: ifaddrs? = nil
    var success: Int = 0
    // retrieve the current interfaces - returns 0 on success
    success = getifaddrs(interfaces)
    if success == 0 
        // Loop through linked list of interfaces
        temp_addr = interfaces
        while temp_addr != nil 
            if temp_addr?.ifa_addr?.sa_family == AF_INET 
                // Check if interface is en0 which is the wifi connection on the iPhone
                if (String(utf8String: temp_addr?.ifa_name) == "en0") 
                    // Get NSString from C String
                    address = String(utf8String: inet_ntoa((temp_addr?.ifa_addr as? sockaddr_in)?.sin_addr))
                
            
            temp_addr = temp_addr?.ifa_next
        
    
        // Free memory
    freeifaddrs(interfaces)
    return address

【讨论】:

【参考方案5】:

Swift 4 - 获取设备的 IP 地址:

在您的桥接头中添加#include&lt;ifaddrs.h&gt;

这是获取 IP 地址所需的框架。

class func getIPAddress() -> String? 
        var address: String?
        var ifaddr: UnsafeMutablePointer<ifaddrs>? = nil
        if getifaddrs(&ifaddr) == 0 
            var ptr = ifaddr
            while ptr != nil 
                defer  ptr = ptr?.pointee.ifa_next 

                let interface = ptr?.pointee
                let addrFamily = interface?.ifa_addr.pointee.sa_family
                if addrFamily == UInt8(AF_INET) || addrFamily == UInt8(AF_INET6) 

                    if let name: String = String(cString: (interface?.ifa_name)!), name == "en0" 
                        var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
                        getnameinfo(interface?.ifa_addr, socklen_t((interface?.ifa_addr.pointee.sa_len)!), &hostname, socklen_t(hostname.count), nil, socklen_t(0), NI_NUMERICHOST)
                        address = String(cString: hostname)
                    
                
            
            freeifaddrs(ifaddr)
        
        return address

【讨论】:

获取“用于检查可选项的“字符串”类型的非可选表达式”警告。 当我激活我的个人热点时,我的地址 === " "(即为空)但是当我的设备被激活到外部热点时工作正常。我正在使用 iOS 12.1.4 嗨。我在尝试 UInt8(...) 方法时崩溃了,有人可以帮忙吗? ***.com/questions/55491986/…【参考方案6】:

这里的所有答案都只提供 wifi 的 IP 地址,而不是有线或蜂窝。以下 sn-p 可用于 wifi/有线/蜂窝案例:

func getIPAddressForCellOrWireless()-> String? 

    let WIFI_IF : [String] = ["en0"]
    let KNOWN_WIRED_IFS : [String] = ["en2", "en3", "en4"]
    let KNOWN_CELL_IFS : [String] = ["pdp_ip0","pdp_ip1","pdp_ip2","pdp_ip3"]

    var addresses : [String : String] = ["wireless":"",
                                         "wired":"",
                                         "cell":""]

    var address: String?
    var ifaddr: UnsafeMutablePointer<ifaddrs>? = nil
    if getifaddrs(&ifaddr) == 0 

        var ptr = ifaddr
        while ptr != nil 
            defer  ptr = ptr?.pointee.ifa_next  // memory has been renamed to pointee in swift 3 so changed memory to pointee

            let interface = ptr?.pointee
            let addrFamily = interface?.ifa_addr.pointee.sa_family
            if addrFamily == UInt8(AF_INET) || addrFamily == UInt8(AF_INET6) 

                if let name: String = String(cString: (interface?.ifa_name)!), (WIFI_IF.contains(name) || KNOWN_WIRED_IFS.contains(name) || KNOWN_CELL_IFS.contains(name)) 

                    // String.fromCString() is deprecated in Swift 3. So use the following code inorder to get the exact IP Address.
                    var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
                    getnameinfo(interface?.ifa_addr, socklen_t((interface?.ifa_addr.pointee.sa_len)!), &hostname, socklen_t(hostname.count), nil, socklen_t(0), NI_NUMERICHOST)
                    address = String(cString: hostname)
                    if WIFI_IF.contains(name)
                        addresses["wireless"] =  address
                    else if KNOWN_WIRED_IFS.contains(name)
                        addresses["wired"] =  address
                    else if KNOWN_CELL_IFS.contains(name)
                        addresses["cell"] =  address
                    
                

            
        
    
    freeifaddrs(ifaddr)

    var ipAddressString : String?
    let wirelessString = addresses["wireless"]
    let wiredString = addresses["wired"]
    let cellString = addresses["cell"]
    if let wirelessString = wirelessString, wirelessString.count > 0
        ipAddressString = wirelessString
    else if let wiredString = wiredString, wiredString.count > 0
        ipAddressString = wiredString
    else if let cellString = cellString, cellString.count > 0
        ipAddressString = cellString
    
    return ipAddressString

【讨论】:

请注意,该问题专门询问“我只需要获取 WiFi IP 地址...”【参考方案7】:

Swift 4.2 UIDevice 扩展,可避免强制解包并支持蜂窝和有线 IP 地址:

import UIKit

extension UIDevice 

    private struct InterfaceNames 
        static let wifi = ["en0"]
        static let wired = ["en2", "en3", "en4"]
        static let cellular = ["pdp_ip0","pdp_ip1","pdp_ip2","pdp_ip3"]
        static let supported = wifi + wired + cellular
    

    func ipAddress() -> String? 
        var ipAddress: String?
        var ifaddr: UnsafeMutablePointer<ifaddrs>?

        if getifaddrs(&ifaddr) == 0 
            var pointer = ifaddr

            while pointer != nil 
                defer  pointer = pointer?.pointee.ifa_next 

                guard
                    let interface = pointer?.pointee,
                    interface.ifa_addr.pointee.sa_family == UInt8(AF_INET) || interface.ifa_addr.pointee.sa_family == UInt8(AF_INET6),
                    let interfaceName = interface.ifa_name,
                    let interfaceNameFormatted = String(cString: interfaceName, encoding: .utf8),
                    InterfaceNames.supported.contains(interfaceNameFormatted)
                    else  continue 

                var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))

                getnameinfo(interface.ifa_addr,
                            socklen_t(interface.ifa_addr.pointee.sa_len),
                            &hostname,
                            socklen_t(hostname.count),
                            nil,
                            socklen_t(0),
                            NI_NUMERICHOST)

                guard
                    let formattedIpAddress = String(cString: hostname, encoding: .utf8),
                    !formattedIpAddress.isEmpty
                    else  continue 

                ipAddress = formattedIpAddress
                break
            

            freeifaddrs(ifaddr)
        

        return ipAddress
    


用法:

UIDevice.current.ipAddress()

【讨论】:

我越来越喜欢这个 fe60::1341:942f:eb20:ff0%en0 任何解决方案???? 这是一个 v6 IP @SreekanthG 并且是预期的。 我在我的设备上获取这种地址 2409:4043:986:e121:6449:eae9:c78f:1042。对吗???【参考方案8】:

如果您只想将 IPv4 响应作为输出,只需修改 Martin R 的解决方案。

 func getWiFiAddress() -> String? 
    var address : String?

    // Get list of all interfaces on the local machine:
    var ifaddr : UnsafeMutablePointer<ifaddrs>?
    guard getifaddrs(&ifaddr) == 0 else  return nil 
    guard let firstAddr = ifaddr else  return nil 

    // For each interface ...
    for ifptr in sequence(first: firstAddr, next:  $0.pointee.ifa_next ) 
        let interface = ifptr.pointee

        // Check for IPv4 or IPv6 interface:
        let addrFamily = interface.ifa_addr.pointee.sa_family
        //if addrFamily == UInt8(AF_INET) || addrFamily == UInt8(AF_INET6)   // **ipv6 committed
        if addrFamily == UInt8(AF_INET)

            // Check interface name:
            let name = String(cString: interface.ifa_name)
            if  name == "en0" 

                // Convert interface address to a human readable string:
                var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
                getnameinfo(interface.ifa_addr, socklen_t(interface.ifa_addr.pointee.sa_len),
                            &hostname, socklen_t(hostname.count),
                            nil, socklen_t(0), NI_NUMERICHOST)
                address = String(cString: hostname)
            
        
    
    freeifaddrs(ifaddr)

    return address

用法:

if let addr = getWiFiAddress() 
   print(addr)
 else 
   print("No WiFi address")

【讨论】:

当我激活我的个人热点时,我的address === " "(即为空)但是当我的设备被激活到外部热点时工作正常。我正在使用 iOS 12.1.4。 不确定这个问题,我有 4 个设备 ios 12.1,1 个设备 12.2,然后 2 个设备出错 -> ***.com/questions/55491986/…【参考方案9】:

对于 Mac 上的 Swift - Swift 4: 这样您还可以从 Wifi 中找出 ip(不仅是以太网)

func getWiFiAddress() -> String? 
    var address : String?

    // Get list of all interfaces on the local machine:
    var ifaddr : UnsafeMutablePointer<ifaddrs>?
    guard getifaddrs(&ifaddr) == 0 else  return nil 
    guard let firstAddr = ifaddr else  return nil 

    // For each interface ...
    for ifptr in sequence(first: firstAddr, next:  $0.pointee.ifa_next ) 
        let interface = ifptr.pointee

        // Check for IPv4 or IPv6 interface:
        let addrFamily = interface.ifa_addr.pointee.sa_family
        if addrFamily == UInt8(AF_INET) || addrFamily == UInt8(AF_INET6) 

            // Check interface name:
            let name = String(cString: interface.ifa_name)
            if  name == "en0" 

                // Convert interface address to a human readable string:
                var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
                getnameinfo(interface.ifa_addr, socklen_t(interface.ifa_addr.pointee.sa_len),
                            &hostname, socklen_t(hostname.count),
                            nil, socklen_t(0), NI_NUMERICHOST)
                address = String(cString: hostname)
             else if name == "en1" 
                // Convert interface address to a human readable string:
                var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
                getnameinfo(interface.ifa_addr, socklen_t(interface.ifa_addr.pointee.sa_len),
                            &hostname, socklen_t(hostname.count),
                            nil, socklen_t(1), NI_NUMERICHOST)
                address = String(cString: hostname)
            
        
    
    freeifaddrs(ifaddr)

    return address

【讨论】:

【参考方案10】:

获取 wifi、有线和蜂窝网络的 IPAddress - swift 5

func getIPAddress() -> String 
    var address: String?
    var ifaddr: UnsafeMutablePointer<ifaddrs>? = nil
    if getifaddrs(&ifaddr) == 0 
        var ptr = ifaddr
        while ptr != nil 
            defer  ptr = ptr?.pointee.ifa_next 

            guard let interface = ptr?.pointee else  return "" 
            let addrFamily = interface.ifa_addr.pointee.sa_family
            if addrFamily == UInt8(AF_INET) || addrFamily == UInt8(AF_INET6) 

                // wifi = ["en0"]
                // wired = ["en2", "en3", "en4"]
                // cellular = ["pdp_ip0","pdp_ip1","pdp_ip2","pdp_ip3"]

                let name: String = String(cString: (interface.ifa_name))
                if  name == "en0" || name == "en2" || name == "en3" || name == "en4" || name == "pdp_ip0" || name == "pdp_ip1" || name == "pdp_ip2" || name == "pdp_ip3" 
                    var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
                    getnameinfo(interface.ifa_addr, socklen_t((interface.ifa_addr.pointee.sa_len)), &hostname, socklen_t(hostname.count), nil, socklen_t(0), NI_NUMERICHOST)
                    address = String(cString: hostname)
                
            
        
        freeifaddrs(ifaddr)
    
    return address ?? ""

如何使用

let strIPAddress : String = self.getIPAddress()
print("IPAddress :: \(strIPAddress)")

注意:在您项目的 Bridging-Header 文件中添加以下内容

#include<ifaddrs.h>

【讨论】:

没有错误,没有警告,完美运行。感谢您在 cmets 中提供更多信息。 欢迎......快乐编码 在模拟器中试一次,我想我在真机上测试过。 在设备上我得到这种地址 2409:4043:986:e121:6449:eae9:c78f:1042 是否正确??? 我不必将它包含在我的桥接头中【参考方案11】:

另一种方法有点不同的解决方案是使用Ipify,这是一项免费的轻量级服务,您可以通过 ping 获取您的 IP

这是他们的 Swift 示例:

let url = URL(string: "https://api.ipify.org")

do 
    if let url = url 
        let ipAddress = try String(contentsOf: url)
        print("My public IP address is: " + ipAddress)
    
 catch let error 
    print(error)

【讨论】:

【参考方案12】:

Swift 5 清理

我更新了上面的答案,以消除任何强制展开和一些 SwiftLint 清理。

 class func getIPAddress() -> String? 
        var address: String?
        var ifaddr: UnsafeMutablePointer<ifaddrs>?
        if getifaddrs(&ifaddr) == 0 
            var ptr = ifaddr
            while ptr != nil 
                defer  ptr = ptr?.pointee.ifa_next 
                let interface = ptr?.pointee
                let addrFamily = interface?.ifa_addr.pointee.sa_family
                if addrFamily == UInt8(AF_INET) || addrFamily == UInt8(AF_INET6),
                    let cString = interface?.ifa_name,
                    String(cString: cString) == "en0",
                    let saLen = (interface?.ifa_addr.pointee.sa_len) 
                    var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
                    let ifaAddr = interface?.ifa_addr
                    getnameinfo(ifaAddr,
                                socklen_t(saLen),
                                &hostname,
                                socklen_t(hostname.count),
                                nil,
                                socklen_t(0),
                                NI_NUMERICHOST)
                    address = String(cString: hostname)
                
            
            freeifaddrs(ifaddr)
        
        return address
    

【讨论】:

这有利于 IPV6,并不总是返回 IPV4 地址【参考方案13】:

基于上述部分答案的NWInterface.InterfaceType 的扩展:

import Network

extension NWInterface.InterfaceType 
    var names : [String]? 
        switch self 
        case .wifi: return ["en0"]
        case .wiredEthernet: return ["en2", "en3", "en4"]
        case .cellular: return ["pdp_ip0","pdp_ip1","pdp_ip2","pdp_ip3"]
        default: return nil
        
    

    func address(family: Int32) -> String?
    
        guard let names = names else  return nil 
        var address : String?
        for name in names 
            guard let nameAddress = self.address(family: family, name: name) else  continue 
            address = nameAddress
            break
        
        return address
    

    func address(family: Int32, name: String) -> String? 
        var address: String?

        // Get list of all interfaces on the local machine:
        var ifaddr: UnsafeMutablePointer<ifaddrs>?
        guard getifaddrs(&ifaddr) == 0, let firstAddr = ifaddr else  return nil 

        // For each interface ...
        for ifptr in sequence(first: firstAddr, next:  $0.pointee.ifa_next ) 
            let interface = ifptr.pointee

            let addrFamily = interface.ifa_addr.pointee.sa_family
            if addrFamily == UInt8(family)
            
                // Check interface name:
                if name == String(cString: interface.ifa_name) 
                    // Convert interface address to a human readable string:
                    var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
                    getnameinfo(interface.ifa_addr, socklen_t(interface.ifa_addr.pointee.sa_len),
                                &hostname, socklen_t(hostname.count),
                                nil, socklen_t(0), NI_NUMERICHOST)
                    address = String(cString: hostname)
                
            
        
        freeifaddrs(ifaddr)

        return address
    

    var ipv4 : String?  self.address(family: AF_INET) 
    var ipv6 : String?  self.address(family: AF_INET6) 

用法:

对于蜂窝的 ipv4:NWInterface.InterfaceType.cellular.ipv4

【讨论】:

【参考方案14】:

支持 IPV4 和 IPV6

使其成为 UIDevice 的扩展并使用 UIDevice.current.getIPAdress() 调用

private struct Interfaces 
    // INTERFACCIE SUPPORT
    static let wifi = ["en0"]
    static let cellular = ["pdp_ip0","pdp_ip1","pdp_ip2","pdp_ip3"]
    static let supported = wifi + cellular



func getIPAdress() -> (String?,String?)? 
    var ip4Adress: String?
    var ip6Adress: String?
    var hasAdress: UnsafeMutablePointer<ifaddrs>?


    if getifaddrs(&hasAdress) == 0 
        var pointer = hasAdress

        while pointer != nil 
            defer  pointer = pointer?.pointee.ifa_next

            guard let interface = pointer?.pointee else continue

            // SEARCH FOR IPV4 OR IPV6 IN THE INTERFACE OF THE NODE
            // HERE I'M ALREADY LOOSING MY MIND
            // PRIORITY FOR IPV4 THAN IPV6
            if  interface.ifa_addr.pointee.sa_family == UInt8(AF_INET) 
                guard let ip4 = processInterface(interface: interface) else 
                    continue
                
                ip4Adress = ip4
            

            if interface.ifa_addr.pointee.sa_family == UInt8(AF_INET6) 
                guard let ip6 = processInterface(interface: interface) else 
                    continue
                
                ip6Adress = ip6
            
        
        freeifaddrs(hasAdress)
    
    return (ip4Adress, ip6Adress)




func processInterface(interface: ifaddrs) -> String? 

    var ipAdress: String = ""
    guard
        let interfaceName = interface.ifa_name else return nil
                       guard
                           let interfaceNameFormatted = String(cString: interfaceName, encoding: .utf8) else return nil
    guard Interfaces.supported.contains(interfaceNameFormatted) else return nil

                       var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))

                       print(interfaceNameFormatted)

                       // CONVERT THE SOCKET ADRESS TO A CORRESPONDING HOST AND SERVICE
                       getnameinfo(interface.ifa_addr,
                                   socklen_t(interface.ifa_addr.pointee.sa_len),
                                   &hostname, socklen_t(hostname.count),
                                   nil,
                                   socklen_t(0),
                                   NI_NUMERICHOST)

                       guard let formattedIpAdress = String(cString: hostname, encoding: .utf8) else return nil
                       if !formattedIpAdress.isEmpty 
                           ipAdress = formattedIpAdress
                       
    return ipAdress

【讨论】:

【参考方案15】:
enum Network: String 
    case wifi = "en0"
    case cellular = "pdp_ip0"
//    case en1 = "en1"
//    case lo = "lo0"


// get ipv4 or ipv6 address
extension UIDevice 
    
    func address(family: Int32, for network: Network) -> String? 
        var address: String?

        // Get list of all interfaces on the local machine:
        var ifaddr: UnsafeMutablePointer<ifaddrs>?
        guard getifaddrs(&ifaddr) == 0, let firstAddr = ifaddr else  return nil 

        // For each interface ...
        for ifptr in sequence(first: firstAddr, next:  $0.pointee.ifa_next ) 
            let interface = ifptr.pointee

            let addrFamily = interface.ifa_addr.pointee.sa_family
            if addrFamily == UInt8(family) 
                // Check interface name:
                let name = String(cString: interface.ifa_name)
                if name == network.rawValue 
                    // Convert interface address to a human readable string:
                    var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
                    getnameinfo(interface.ifa_addr, socklen_t(interface.ifa_addr.pointee.sa_len),
                                &hostname, socklen_t(hostname.count),
                                nil, socklen_t(0), NI_NUMERICHOST)
                    address = String(cString: hostname)
                
            
        
        freeifaddrs(ifaddr)

        return address
    
    
    func ipv4(for network: Network) -> String? 
        self.address(family: AF_INET, for: network)
    
    
    func ipv6(for network: Network) -> String? 
        self.address(family: AF_INET6, for: network)
    
    
    // get all addresses
    func getIFAddresses() -> [String] 
        var addresses = [String]()
        
        // Get list of all interfaces on the local machine:
        var ifaddr : UnsafeMutablePointer<ifaddrs>?
        guard getifaddrs(&ifaddr) == 0 else  return [] 
        guard let firstAddr = ifaddr else  return [] 
        
        // For each interface ...
        for ptr in sequence(first: firstAddr, next:  $0.pointee.ifa_next ) 
            let flags = Int32(ptr.pointee.ifa_flags)
            let 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) 
                    
                    // Convert interface address to a human readable string:
                    var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
                    if (getnameinfo(ptr.pointee.ifa_addr, socklen_t(addr.sa_len), &hostname, socklen_t(hostname.count),
                                    nil, socklen_t(0), NI_NUMERICHOST) == 0) 
                        let address = String(cString: hostname)
                        addresses.append(address)
                    
                
            
        
        
        freeifaddrs(ifaddr)
        
        return addresses
    

如何使用

// for wifi
  let wifi = UIDevice.current.ipv4(for: .wifi) # ipv4
  let wifi6 = UIDevice.current.ipv6(for: .wifi) # ipv6

// for cellular
  let cellular = UIDevice.current.ipv4(for: .cellular) # ipv4
  let cellular6 = UIDevice.current.ipv6(for: .cellular) # ipv6

【讨论】:

【参考方案16】:

鉴于您已经拥有套接字的文件描述符,这是 Swift 5.2 的解决方案。

// Depending on your case get the socket's file descriptor
// the way you want
let socketFd = foo()

// Get the remote address for that file descriptor
var addr: sockaddr_storage = sockaddr_storage()
var addr_len: socklen_t = socklen_t(MemoryLayout.size(ofValue: addr))

var hostBuffer = [CChar](repeating: 0, count: Int(NI_MAXHOST))

// Make local copy to avoid: "Overlapping accesses to 'addr', 
// but modification requires exclusive access; consider copying
// to a local variable"
let addrLen = addr.ss_len

withUnsafeMutablePointer(to: &addr) 
    $0.withMemoryRebound(to: sockaddr.self, capacity: 1) 
        if getpeername(socketFd, $0, &addr_len) != 0  return 
        
        getnameinfo($0, socklen_t(addrLen), &hostBuffer, socklen_t(hostBuffer.count), nil, 0, NI_NUMERICHOST)
    


let connectedHost = String(cString: hostBuffer, encoding: .utf8)

【讨论】:

【参考方案17】:

该类使用 NWPathMonitor 监视设备接口。您可以设置一个 updatehandler 来通知您有关接口状态更改的信息。选择是否要跟踪 ipv4 或 ipv6 并检查 status.interfaceType == .wifi 以查看您的活动接口是否为 wifi

用法:

let monitor = IPMonitor(ipType: .ipv4) 
monitor.pathUpdateHandler =  status in 
  print("\(status.debugDescription)")

类:

import Foundation
import Network

class IPMonitor 
    
    enum InterfaceType: String 
        case cellular = "cellular"
        case wifi = "wifi"
        case wired = "wired"
        case loopback = "loopback"
        case other = "other"
        case notFound = "not found"
    
    
    enum IPType: String 
        case ipv4 = "IPv4"
        case ipv6 = "ipV6"
        case unknown = "unknown"
    
    
    struct Status 
        var name = "unknown"
        var interfaceType: InterfaceType = InterfaceType.notFound
        var ip: [String] = []
        var ipType: IPType = IPType.unknown
        
        var debugDescription: String 
            let result = "Interface: \(name)/\(interfaceType.rawValue), \(ipType.rawValue)\(ip.debugDescription)"
            return result
        
    
    
    private let monitor = NWPathMonitor()
    private let queue = DispatchQueue(label: "ip_monitor_queue")
    
    final var pathUpdateHandler: ((Status) -> Void)?
    
    init(ipType: IPType) 
        monitor.pathUpdateHandler =  path in
            let name = self.getInterfaceName(path: path)
            let type = self.getInterfaceType(path: path)
            let ip = self.getIPAddresses(interfaceName: name, ipType: ipType)
            let status = Status(name: name, interfaceType: type, ip: ip, ipType: ipType)
            //print("\(status)")
            self.pathUpdateHandler?(status)
        
        monitor.start(queue: queue)
    
    
    private func getInterfaceName(path: NWPath) -> String 
        if let name = path.availableInterfaces.first?.name 
            return name
        
        return "unknown"
    
    
    private func getInterfaceType(path: NWPath) -> InterfaceType 
        if let type = path.availableInterfaces.first?.type 
            switch type 
            case NWInterface.InterfaceType.cellular:
                return InterfaceType.cellular
            case NWInterface.InterfaceType.wifi:
                return InterfaceType.wifi
            case NWInterface.InterfaceType.wiredEthernet:
                return InterfaceType.wired
            case NWInterface.InterfaceType.loopback:
                return InterfaceType.loopback
            default:
                return InterfaceType.other
            
        
        
        return InterfaceType.notFound
    
    
    private func getIPAddresses(interfaceName: String, ipType: IPType)-> [String]
        var addresses: [String] = []
        var ifaddr: UnsafeMutablePointer<ifaddrs>? = nil
        if getifaddrs(&ifaddr) == 0 
            var ptr = ifaddr
            while ptr != nil 
                defer  ptr = ptr?.pointee.ifa_next 
                let interface = ptr?.pointee
                let addrFamily = interface?.ifa_addr.pointee.sa_family
                if (addrFamily == UInt8(AF_INET) && ipType == .ipv4)
                    || (addrFamily == UInt8(AF_INET6) && ipType == .ipv6) 
                    let name = String(cString: (interface?.ifa_name)!)
                    if name == interfaceName 
                        var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
                        getnameinfo(interface?.ifa_addr, socklen_t((interface?.ifa_addr.pointee.sa_len)!), &hostname, socklen_t(hostname.count), nil, socklen_t(0), NI_NUMERICHOST)
                        addresses.append(String(cString: hostname))
                    
                
            
        
        freeifaddrs(ifaddr)
        
        return addresses
    

【讨论】:

以上是关于Swift - 获取设备的 WIFI IP 地址的主要内容,如果未能解决你的问题,请参考以下文章

电脑连接WIFI热点为啥一直显示正在获取IP地址?

为啥手机连接wifi一直显示正在获取ip地址

如何在同一网络上以编程方式获取其他支持Wifi的设备的IP地址?

电脑连接WIFI热点为啥一直显示正在获取IP地址

在wifi-direct p2p连接中获取对等设备的IP地址[重复]

如何以编程方式通过 Wifi 获取连接在同一网络上的其他设备的 IP 地址?