I have an issue where my iPhone app needs to listen for a UDP packet and print that data to the screen. UDP packets are sent by a PC connecting to a Wifi router and my Iphone is also connecting to it and listening on the correct port. When running the App, it works at the first moment and receives the first UDP packet co, but after receiving the first packet, it stops receiving more and I see errors in the logs. These errors only appear when I test directly on the Iphone receiving packets through the router's wifi network with ip: 192.168.0.1. When I run through the simulator using localhost (127.0.0.1 ) the errors do not appear and I receive all packages correctly. Code for UDP listener:
class UDPConecct: ObservableObject {
var listener: NWListener?
var connection: NWConnection?
var queue = DispatchQueue.global(qos: .userInitiated)
/// 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
var port: NWEndpoint.Port
@Published var receivedWords: [Float64] = []
@Published var isCaptureStarted = false
//@Published var udpPacket: UDP_Packet?
/// 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) {
self.port = port
let params = NWParameters.udp
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: self.queue)
}
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: .global())
}
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.handleCapturedData(data)
if self.listening {
print("escutando")
self.receive()
}
}
}
func handleCapturedData(_ data: Data) {
print("quantidade de dados", data.count)
var value: [Float64] = []
if data.count % 2 != 0 {
let startOffset = 1
let length = (data.count-1)/2
let range = startOffset..<startOffset+length
// Fatiando o Data Recebido via UDP usando o intervalo
let slicedData = data.subdata(in: range)
print("dados cortados:", slicedData)
for i in stride(from: 0, to: slicedData.count, by: 8) {
let startIndex = i
let endIndex = min(i + 8, slicedData.count)
let subarray = Array(slicedData[startIndex..<endIndex])
let floatValue = subarray.withUnsafeBytes { $0.load(as: Float64.self) }
value.append(floatValue)
}
print("Dados Recebidos", value)
self.receivedWords = value
} else {
let startIndex = 14
let endIndex = data.count - 2
let relevantData = Array(data[startIndex..<endIndex])
// Iterar sobre os dados de 16 bits e convertê-los para Double
for i in stride(from: 0, to: relevantData.count, by: 2) {
let byte1 = relevantData[i]
let byte2 = relevantData[i + 1]
let wordValue = UInt16(byte1) << 8 | UInt16(byte2)
let doubleValue = Double(wordValue)
value.append(doubleValue)
}
self.receivedWords = value
print("Dados Recebidos 2", value)
}
}
func cancel() {
self.listening = false
self.isCaptureStarted = false
self.connection?.cancel()
}
}
Network framework is not able to handle all possible UDP broadcast architectures, something I called out in TN3151 Choosing the right networking API:
Network framework supports UDP multicast using the NWConnectionGroup class, but that support has limits and, specifically, it does not support UDP broadcast. If you need something that’s not supported by Network framework, use BSD Sockets.
To work with UDP broadcasts or multicasts on iOS, you need to apply for, and be granted access to, the multicast networking managed entitlement. For more on that whole issue, see Local Network Privacy FAQ.
I don't have a paid Apple Developer license yet
That will be a problem; there’s no way to get that entitlement when using free provisioning (aka a Personal Team).
What are you trying to do with broadcasts? I see a lot of folks go down this path for the wrong reasons [1].
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
[1] The most common wrong reason is that they’re implementing a custom service discovery protocol. In most cases you’re better off using Bonjour.
The second wrong reason is that they think it’ll be faster. Due to the way that Wi-Fi works, that’s often not the case. For more on this, see Wi-Fi Fundamentals.