NWConnection, how to catch error?

I have a NWConnection, that if an invalid (ip/port), is entered, I get in debug the error: nw_socket_handle_socket_event [C1.1.1:1] Socket SO_ERROR 61. But I can't seem to trap that error. I have as my stateChangeHandler:

I am creating my connection:

        let tcpOptions = NWProtocolTCP.Options()
        tcpOptions.enableKeepalive = true
        tcpOptions.keepaliveIdle = 2
        tcpOptions.keepaliveCount = 2
        tcpOptions.keepaliveInterval = 2
        let params = NWParameters(tls: nil, tcp: tcpOptions)
nwConnection:NWConnection(host: NWEndpoint.Host(host), port: NWEndpoint.Port(port)!, using: params). (with known nonexistent ip/port).

I was hopping when I did a .start(), I would get an error in my state handler:

    // ===============================================================================
    func stateDidChange(to state: NWConnection.State) {
        Swift.print("State change")
        switch state {
        case .waiting(let error):
            print("Client waiting")
           connectionDidFail(error: error)
        case .ready:
            print("Client connection ready")
        case .failed(let error):
            print("Client failed")
            connectionDidFail(error: error)
        case .preparing:
            print("client preparing")
        case .setup:
            print("client setup")
        case .cancelled:
            print("client cancelled")
        default:
            print("Client unknown")

            break
        }
    }

But it doesn't trap an error. So, where is this error coming from (I know the cause), but I want to trap it (in case a user puts in a wrong ip/port)

Consider this test code:

import Foundation
import Network

func main() {
    let c = NWConnection(host: "fluffy.local.", port: 80, using: .tcp)
    c.stateUpdateHandler = { newState in
        print(newState)
    }
    c.start(queue: .main)
    withExtendedLifetime(c) {
        dispatchMain()
    }
}

main()

It prints:

preparing
ready

because Fluffy is listening on port 80. However, if I change the 80 to an 81, I get this:

preparing
waiting(POSIXErrorCode(rawValue: 61): Connection refused)

If I then change fluffy.local. to example.com I get this:

preparing

which seems to be what you’re seeing. However, that’s because example.com never sends me the RST packet:

% sudo tcpdump -i en0 -n port 81
…
… 192.168.1.171.61093 > 93.184.216.34.81: Flags [S], seq 3653351410, …
… 192.168.1.171.64634 > 93.184.216.34.81: Flags [S], seq 2521652961, …
… 192.168.1.171.64634 > 93.184.216.34.81: Flags [S], seq 2521652961, …
… 192.168.1.171.64634 > 93.184.216.34.81: Flags [S], seq 2521652961, …
… 192.168.1.171.64634 > 93.184.216.34.81: Flags [S], seq 2521652961, …
^C

so my Mac is continually trying to establish the connection. I see the same thing if I use BSD Sockets:

% telnet example.com 81
Trying 93.184.216.34...
… nothing else …

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thank you so much. I have a question, I have never used: withExtendedLifetime. When would that make sense to use? Should I do it for all my network conditions (given I always am using tcp (not web services).

I have never used: withExtendedLifetime(…). When would that make sense to use?

The general rule with Network framework is that, if you release your last reference to an object, the object cancels itself. For an NWConnection, that means that the underlying connection closes. You don’t want that to happen, so you have to maintain a strong reference.

In Real Code™, you end up maintaining a reference to the connection object naturally. For example, you might have a connection, a buffer associated with that connection, info about the connection state, and so on. A good option is to gather all of that together in your own object. As long as you keep that object around, the NWConnection stays around.

In a tiny test project like this, there’s no other connection state, just the connection object. So, to keep that around I use withExtendedLifetime(…).

So:

  • In this test project I need withExtendedLifetime(…).

  • In a real app that’s rarely necessary.

  • But you do need to make sure you maintain a strong reference to the NWConnection.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

NWConnection, how to catch error?
 
 
Q