I'm using NWListener with NWConnection. This code work great and I am able to start the listener and successfully receive connections in Xcode preview.
However, when I build/run on my phone, the listener does not seem to accept connections from the network. If the app sends a message to itself on the phone, it is received, but if I send a message to that ip/port from another network device, it is not accepted/received.
Any help or suggestions appreciated.
import Foundation
import Network
import Combine
@Observable
class UDPListener3 {
var listener: NWListener?
var queue = DispatchQueue.global(qos: .userInitiated)
var messageReceived: Data?
private(set) var isReady: Bool
private(set) var listening: Bool = false
private(set) var receiving: Bool = false
private(set) var port: UInt16 = 0
init () {
isReady = false
listening = false
receiving = false
messageReceived = nil
listener = nil
}
func GetPort() {
var portText = "\(String(describing: listener!.port))"
portText = portText.replacingOccurrences(of: "Optional(", with: "")
portText = portText.replacingOccurrences(of: ")", with: "")
portText = portText.replacingOccurrences(of: ",", with: "")
port = UInt16(portText)!
if let testPort = listener?.port?.rawValue {
self.port = testPort
}
}
func config(port: Int) {
if port > 0 {
configinit(port: NWEndpoint.Port(integerLiteral: NWEndpoint.Port.IntegerLiteralType(port)))
} else {
configinit(port: nil)
}
}
func configinit(port: NWEndpoint.Port?) {
// conifigure and create listener
let params = NWParameters.tcp
params.allowFastOpen = true
if port == nil {
self.listener = try? NWListener(using: params, on: .any)//port)
} else {
self.listener = try? NWListener(using: params, on: port!)
}
if listener != nil {
GetPort()
self.listening = true
self.listener?.stateUpdateHandler = { [self] update in
switch update {
case .ready:
self.isReady = true
print("*Listener.ready on port \(String(describing: self.listener?.port))")
GetPort()
case .failed:
// Announce we are no longer able to listen
self.listening = false
self.isReady = false
print("*Listener.failed on port \(port)")
case .cancelled:
// Announce we are no longer able to listen
self.listening = false
self.isReady = false
print("*Listener.canceled on port \(port)")
default:
print("*Listener default connecting to port \(port)... \(self.listener!.state)")
}
print()
}
self.listener?.newConnectionHandler = { connection in
print("@called listener.newConnectionHandler")
self.createConnection(connection: connection)
}
// start listening
self.listener?.start(queue: self.queue)
GetPort()
} else {
print("unable to start listener")
}
}
func createConnection(connection: NWConnection) {
connection.stateUpdateHandler = { (newState) in
switch (newState) {
case .ready:
print(" ...Connection.ready")// - \(connection)")
self.receive(connection)
case .cancelled:
print(" ...Connection.cancelled")// - \(connection)")
case .failed:
print(" ...Connection.failed")// - \(connection)")
default:
print(" ...Connection.default: \(connection.state)")// - \(connection)")
}
}
print(" ...connection starting")
connection.start(queue: .global())
}
func receive(_ connection: NWConnection) {
print()
print(" ...connection receiving")
// respond 200 received
self.respond(on: connection)
//connection.receiveMessage() { [self] data, context, isComplete, error in //<-- this would not return until timeout expired??
connection.receive(minimumIncompleteLength: 20000, maximumLength: 200000) { [self] data, context, isComplete, error in
receiving = true
/* Check what we have */
var message = ""
if data != nil {
message = String(decoding: data!, as: UTF8.self)
} else {
message = ""
}
// ERROR
if let unwrappedError = error {
print(" >>ERROR: received in \(#function) - \(unwrappedError)")
receiving = false
return
}
// NO DATA
guard let data = data else {
print(" >>NO DATA with context - \(String(describing: context))")
receiving = false
return
}
// NOT COMPLETE
if !isComplete {
print(" >>NOT COMPLETE with context - \(String(describing: context))")
//return
}
// RECEIVED A MESSAGE
if message != "" {
self.messageReceived = data
print(" ...received data - \(String(describing: context)) \(String(describing: data))")
receiving = false
connection.cancel()
return
}
// keep receiving,
self.receive(connection)
}
}
}
func respond(on connection: NWConnection) {
let response = """
HTTP/1.1 200 OK
Content-Length: 2
OK
"""
connection.send(
content: response.data(using: .utf8),
completion: .idempotent
)
}
func cancel() {
self.listener?.cancel()
self.listener = nil
self.listening = false
self.isReady = false
print("listener disabled")
}
}