I have an issue where my iPhone app needs to listen for a UDP packet, and respond to the sender. This works when I first load the app, but after receiving the first packet and responding, it stops receiving any more, and I instead see errors in the logs.
Code for UDP Listener:
import Foundation
import Network
import Combine
class UDPListener: ObservableObject {
var listener: NWListener?
var connection: NWConnection?
/// New data will be place in this variable to be received by observers
@Published private(set) public var messageReceived: Data?
/// When there is an active listening NWConnection this will be `true`
@Published private(set) public var isReady: Bool = false
/// Default value `true`, this will become false if the UDPListener ceases listening for any reason
@Published public var listening: Bool = true
/// A convenience init using Int instead of NWEndpoint.Port
convenience init(on port: Int) {
self.init(
on: NWEndpoint.Port(integerLiteral: NWEndpoint.Port.IntegerLiteralType(port))
)
}
/// Use this init or the one that takes an Int to start the listener
init(on port: NWEndpoint.Port) {
let params = NWParameters.udp
params.allowLocalEndpointReuse = true
params.allowFastOpen = true
self.listener = try? NWListener(using: params, on: port)
self.listener?.stateUpdateHandler = { update in
switch update {
case .ready:
self.isReady = true
print("Listener connected to port \(port)")
case .failed, .cancelled:
// Announce we are no longer able to listen
self.listening = false
self.isReady = false
print("Listener disconnected from port \(port)")
default:
print("Listener connecting to port \(port)...")
}
}
self.listener?.newConnectionHandler = { connection in
print("Listener receiving new message")
self.createConnection(connection: connection)
}
self.listener?.start(queue: .main)
}
func createConnection(connection: NWConnection) {
self.connection = connection
self.connection?.stateUpdateHandler = { (newState) in
switch (newState) {
case .ready:
print("Listener ready to receive message - \(connection)")
self.receive()
case .cancelled, .failed:
print("Listener failed to receive message - \(connection)")
// Cancel the listener, something went wrong
self.listener?.cancel()
// Announce we are no longer able to listen
self.listening = false
default:
print("Listener waiting to receive message - \(connection)")
}
}
self.connection?.start(queue: .main)
}
func receive() {
self.connection?.receiveMessage { data, context, isComplete, error in
if let unwrappedError = error {
print("Error: NWError received in \(#function) - \(unwrappedError)")
return
}
guard isComplete, let data = data else {
print("Error: Received nil Data with context - \(String(describing: context))")
return
}
self.messageReceived = data
print(String(decoding: data, as: UTF8.self))
if String(decoding: data, as: UTF8.self) == "teststring" {
switch self.connection?.endpoint {
case .hostPort(let host, let port):
print("Sending response to \(host):\(port)")
default:
break
}
self.connection?.send(
content: String("received").data(using: .utf8),
completion: NWConnection.SendCompletion.contentProcessed(({ (NWError) in
if (NWError == nil) {
print("Data was sent to UDP")
} else {
print("ERROR SEND! Error when data (Type: Data) sending. NWError: \n \(NWError!)")
}
}))
)
}
if self.listening {
self.receive()
}
print(String(self.listening))
}
}
func cancel() {
print("cancelling udp listener")
self.listening = false
self.connection?.cancel()
}
}