Search code examples
iosswiftuikitip-addressnwpathmonitor

NWEndPoint: it returns nil when getting gateway's IP address from another VC


I has found the solution to this question, see below answer.

I define a func getGatewayInfo() to get the Gateway IP Address and return it to the caller. But when I call this func print(NetworkUtility().getGatewayInfo()) from other VC's viewDidLoad, it returns nil.

From the log, I could see gatewayIPAddress has got the value from NWEndpoint, which is 192.168.1.1. However, it returns nil and print out "Gateway IP address is not found!". Could u give me some hint where I did wrong?

Gateway IP address is not found!
Gateway: 192.168.1.1
import UIKit
import Network

class NetworkUtility {
    var gatewayIPAddress: String?
    
    func getGatewayInfo() -> String {
        let monitor = NWPathMonitor(requiredInterfaceType: .wifi)
        monitor.pathUpdateHandler = { path in
            let endpoint = path.gateways[0]
            
            switch endpoint {
            case .hostPort(let host, _):
                self.gatewayIPAddress = host.debugDescription
                print("Gateway: \(self.gatewayIPAddress!)")
            default:
                break
            }
        }
        monitor.start(queue: DispatchQueue(label: "nwpathmonitor.queue"))
        
        if let gatewayIPAddress = gatewayIPAddress {
            return gatewayIPAddress
        } else {
            return "Gateway IP address is not found!"
        }
    }
}

Solution

  • The problem here is the handler code (closure) in monitor.pathUpdateHandler path in is asynchronous executed, so the return statement in the previous code will be executed before it. Eventually, the returned parameter is nil.

    And since we don't know when the code in closure will be finished like some networking request. So we cannot using return method in this function. Instead, we should use another completion handler to return the param value, which is callback. Like what we do in some JSON request functions.

    Code to involve the another completionHandler in case to return the string parameter:

    func getGatewayInfo(completionHandler: @escaping (String) -> ()) {
        let monitor = NWPathMonitor(requiredInterfaceType: .wifi)
        monitor.pathUpdateHandler = { path in
            if let endpoint = path.gateways.first {
                switch endpoint {
                case .hostPort(let host, _):
                    let remoteHost = host.debugDescription
                    print("Gateway: \(remoteHost)")
                    // Use callback here to return the ip address to the caller
                    completionHandler(remoteHost)
                default:
                    break
                }
            } else {
                print("Wifi connection may be dropped.")
            }
        }
        monitor.start(queue: DispatchQueue(label: "nwpathmonitor.queue"))
    }
    

    The caller:

    override func viewDidLoad() {
        super.viewDidLoad()
        title = pageTitle
        
        // Receive remoteHost from callback parameter(aka. gateway ip address)
        NetworkUtility().getGatewayInfo { (remoteHost) in
            print("remote: \(remoteHost)")
            DispatchQueue.main.async {
                self.gwIPAddress.text = remoteHost
            }
        }
    }