NWParemeters.requiredLocalEndpoint what is it used for?

I've just started working on an app that communicates with another device via tcp and I have some doubt.


1) Sometimes the device(server) and the users phone are connected to the same local network and sometimes they are not, when they are I need to use it's local address and when they aren't it's public. How do I handle those transitions ?(eg: when the user walks out of the wireless range)

Is that what the requiredLocalEndpoint is for? I couldn't make it out from the documentation.


2) How to use the betterPathUpdateHandler? I'm pretty sure the server does not support multipath tcp but I need constant communication with it, how do I handle changes between cellular and wireless connection?


My code so far is the following

class CrestronNetwork {    
    let endpoint = NWEndpoint.hostPort(host: “example.com", port: 11200)
    let localEndpoint = NWEndpoint.hostPort(host: "192.168.20.15”, port: 11200)

    let parameters = NWParameters.tcp
    var connection: NWConnection!
    let myQueue = DispatchQueue(label: "Network Queue")
    var delegate: NetworkDelegate?
    
    init() {
        parameters.multipathServiceType = .disabled
        parameters.expiredDNSBehavior = .allow
        parameters.requiredLocalEndpoint = localEndpoint
        
        connection = create(endpoint)
    }
    
    func create(_ myEndpoint:NWEndpoint) -> NWConnection {
        let newConnection:NWConnection
        
        newConnection = NWConnection(to: endpoint, using: parameters)
        
        newConnection.stateUpdateHandler = {
            (newState) in switch (newState) {
            case .ready:
                self.debug("ready")
                self.connection = newConnection
                self.sendMsg(message: "0-1")
                self.receive(on: newConnection)
              
                self.lookForBetterPath(on: newConnection

            case .waiting(let error):
                self.debugPrint(“Waiting error: \(error)")

            case .failed(let error):
                self.debugPrint(“Failed: \(error)")
                self.start()

            case .cancelled:
                self.debugPrint(“Cancelled")

            default:
                break
            }
        }
        
        newConnection.start(queue: self.myQueue)
        
        return newConnection
    }
    
    
    func lookForBetterPath(on con:NWConnection) {
        con.betterPathUpdateHandler = { (betterPathAvailable) in
            if (betterPathAvailable) {
                self.debugPrint("Better path available")
                _ = self.start
            }
        }
    }
    
    func start() {
        _ = create(endpoint)
    }
    
    func isReady() -> Bool {
        if(connection.state == .ready) {
            return true
        }
        return false
    }
    
    func sendMsg(message: String) {
        let msg = message + "\r\n"
        let data: Data? = msg.data(using: .utf8)
        connection.send(content: data, completion: .contentProcessed { (sendError) in
            if let sendError = sendError {
                self.debugPrint(“\(sendError)")
            }
        })
    }
    
    func receive(on con: NWConnection) {
        con.receive(minimumIncompleteLength: 1, maximumLength: 8192) { (content, context, isComplete, error) in
            if let content = content {
                DispatchQueue.main.async {
                    self.tellViewController(message: String(decoding: content, as: UTF8.self))
                }
           }
            if con.state == .ready && isComplete == false {
                self.receive(on: con)
            }
        }
    }
    
    func cancel() {
        connection.cancel()
    }
    
    func tellViewController(message: String) {
        DispatchQueue.main.async {
            self.delegate?.passData(data: message)
        }
    }
}

Accepted Reply

I need to clarify your requirements, so let’s start with your first question:

Sometimes the device (server) and the users phone are connected to the same local network and sometimes they are not, when they are I need to use it’s local address and when they aren't it’s public

I’m presuming that the client is running on iOS and the server running on some non-Apple platform [1]. Correct?

In the above quote, which end does “it’s” refer to here?

Is that what the

requiredLocalEndpoint
is for?
requiredLocalEndpoint
allows you to specify the local addressed used by a connection. It’s similar to the way that you might call
bind
before calling
connect
in BSD Sockets. However, there’s a subtlety that you need to understand. In BSD Sockets it’s traditional to bind to a specific local address on an outgoing TCP connection for two reasons:
  • To explicitly select a local address or port number or both (A)

  • To implicitly choose an interface (B).

requiredLocalEndpoint
is the equivalent to A but Network framework has better options for B [2]. Specifically, you can control the interface via the
requiredInterface
,
prohibitedInterfaces
,
requiredInterfaceType
and
prohibitedInterfaceTypes
properties on
NWParameters
.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

[1] It’s best to avoid the word device in contexts like this, because it’s common for folks to use device to refer their iOS device.

[2] Modern versions of BSD Sockets also support

IP_BOUND_IF
for explicit interface selection.

Replies

I need to clarify your requirements, so let’s start with your first question:

Sometimes the device (server) and the users phone are connected to the same local network and sometimes they are not, when they are I need to use it’s local address and when they aren't it’s public

I’m presuming that the client is running on iOS and the server running on some non-Apple platform [1]. Correct?

In the above quote, which end does “it’s” refer to here?

Is that what the

requiredLocalEndpoint
is for?
requiredLocalEndpoint
allows you to specify the local addressed used by a connection. It’s similar to the way that you might call
bind
before calling
connect
in BSD Sockets. However, there’s a subtlety that you need to understand. In BSD Sockets it’s traditional to bind to a specific local address on an outgoing TCP connection for two reasons:
  • To explicitly select a local address or port number or both (A)

  • To implicitly choose an interface (B).

requiredLocalEndpoint
is the equivalent to A but Network framework has better options for B [2]. Specifically, you can control the interface via the
requiredInterface
,
prohibitedInterfaces
,
requiredInterfaceType
and
prohibitedInterfaceTypes
properties on
NWParameters
.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

[1] It’s best to avoid the word device in contexts like this, because it’s common for folks to use device to refer their iOS device.

[2] Modern versions of BSD Sockets also support

IP_BOUND_IF
for explicit interface selection.

Thanks, you're correct the client is iOS and the server is not, thanks for the explanation that parameter is definetily not what I'm looking for but its always good to know what it actually does.


I'm going to be more specific. I'm working on a home automation app, the server is a central that controls lights, shutters, etc...

So given this the user (iOS) is going to access the server through the LAN and when it does so it needs to use the server's local ip but when it loses signal (or is trying to access it away from home) it needs to quickly fall back to its public ip since this kind of all would require real time interactions. What's the best way to do this without (can't change any lan config)? Should I just start 2 connections every time? 1 with the local ip and 1 with public?

What's the best way to do this without (can't change any lan config)?

NWConnection
would only be able to help here if your server supported MPTCP (Multipath TCP). [On further consideration, I’m not sure that this is true. It’s not relevant to tar rocha’s scenario anyway.]

For standard TCP,

NWConnection
has complex logic establishing a connection to the best IP address for a given server but, once the connection is in place, it’s always to a specific IP address. This is fundamental to the TCP protocol itself: TCP connections are uniquely identified by the tuple srcIP + srcPort + dstIP + dstPort.

There’s nothing stopping you from running multiple connections to the server simultaneously — one over Wi-Fi to the local IP address and one over WWAN to the public IP address — but that’s something you’d have to code up yourself.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Yes, thank you. I had already tried estabilishing both connections at the same time and just letting 1 fail. It seems to be working fine, I was just wondering if there was a better way.