isComplete in NWConnection.receive never becomes true

I am using NWconnection to force some requests to go through cellular data. Everything works fine, except I keep calling connection.receive and don't know when the transfer is complete.

// Setting up the connection:
     let tcpOptions = NWProtocolTCP.Options()
     let parameters = NWParameters(tls: .init(), tcp: tcpOptions)
     parameters.requiredInterfaceType = .cellular
     port = port ?? NWEndpoint.Port(443)

// ...

   NWConnection(to: .hostPort(host: NWEndpoint.Host(host),port: port), using: parameters)

// Receiving data once the connection is ready
    func receive(withConnection connection: NWConnection, completion: @escaping dataCompletion) {
        connection.receive(minimumIncompleteLength: 1, maximumLength: Int.max) { [self] data, contentContext, isComplete, error in
            if let data = data {                
                receivedData.append(data)                
                print(contentContext)
                if isComplete || error != nil  || connection.state != .ready {                    
                    cancelConnection(connection)
                } else {
                    processor?.receive(withConnection: connection, completion: completion)
                }
            } else {                
                cancelConnection(connection)
            }
        }
    }

I am consuming regular HTTP endpoints. The data in receive comes in chunks but isComplete parameter never becomes true, so I don't know when to stop and send the complete receivedData back.

I've thought of adding a timeout, but it seems kind of hacky. Is there a better solution?

I am consuming regular HTTP endpoints. The data in receive comes in chunks but isComplete parameter never becomes true, so I don't know when to stop and send the complete receivedData back.

I think you've misunderstood what "isComplete" means in the context of Network.framework. NSConnection is only handling the TCP connection, but TCP in an unbounded bytestream protocol. There aren't any "messages" at the TCP layer, so there isn't anything to "complete". This is stated more directly in the documentation for "receiveMessage(completion:)":

"If you request to receive a message on a protocol that is otherwise an unbounded bytestream, like TCP or TLS, note that this will not deliver any data until the stream is closed by the peer."

If the server didn't close the connection (this is common for modern HTTP servers), then you won't get "isComplete".

I've thought of adding a timeout, but it seems kind of hacky. Is there a better solution?

Yes. You need to process the data you've received, parse the HTTP data, and then make your decisions based on what that data tells you.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Thanks Kevin, I am new to this so yes I might have misunderstood some aspects of it. Given that I am making HTTP requests I think I can use Content-length to determine when the HTTP message is complete. However I am not sure if that is enough because some servers might nor send that header. Wish there was like and EOF character for HTTP messages that I can search for.

I am using NWConnection to force some requests to go through cellular data.

Why are you doing that?

Implementing your own HTTP client is hard and, in general, it’s best to rely on the battle-tested code within URLSession. Sadly, URLSession doesn’t offer a way to force a request to run over a specific interface, so I’d like to know more about why you need that before I suggest a path forward.

Share and Enjoy

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

isComplete in NWConnection.receive never becomes true
 
 
Q