Skip to content

Instantly share code, notes, and snippets.

@michael94ellis
Created December 18, 2021 23:47
Show Gist options
  • Save michael94ellis/92828bba252ccabd071279be098e26e6 to your computer and use it in GitHub Desktop.
Save michael94ellis/92828bba252ccabd071279be098e26e6 to your computer and use it in GitHub Desktop.
//
// UDPListener.swift
//
// Created by Michael Robert Ellis on 12/16/21.
//
import Foundation
import Network
import Combine
class UDPListener: 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
/// 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.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.messageReceived = data
if self.listening {
self.receive()
}
}
}
func cancel() {
self.listening = false
self.connection?.cancel()
}
}
@2210Hansen
Copy link

Hi Micheal.
As many others, i'm a newbe, but hope you can shoot me in the right direction.
I need to build a app with UDP connections to 3 or more hosts.
I have ended up with your class.
my problem is that i need to detect which host, there is returning packets.
I made 3 connections with
@State var connection1: NWConnection?
@State var connection2: NWConnection?
@State var connection3: NWConnection?
and
connection1 = NWConnection(host: host1, port: port, using: .udp)

    connection1!.stateUpdateHandler = { (newState) in
        switch (newState) {
        case .preparing:
            NSLog("Entered state: preparing")
        case .ready:
            NSLog("Entered state: ready")
            
        case .setup:
            NSLog("Entered state: setup")
        case .cancelled:
            NSLog("Entered state: cancelled")
        case .waiting:
            NSLog("Entered state: waiting")
        case .failed:
            NSLog("Entered state: failed")
        default:
            NSLog("Entered an unknown state")
        }
    }
    
    connection1!.viabilityUpdateHandler = { (isViable) in
        if (isViable) {
            NSLog("Connection is viable")
            connected = true
        } else {
            NSLog("Connection is not viable")
        }
    }

i send data to host with..........
func send1(_ payload: String) {
let myPayload = Data(payload.utf8)
connection1!.send(content: myPayload, completion: .contentProcessed({ sendError in
if let error = sendError {
NSLog("Unable to process and send the data: (error)")
} else {
NSLog("Data has been sent to 1")
print(payload)
connection1!.receiveMessage { (data, context, isComplete, error) in
guard let myData = data else {
print("Der er modtage fejl")
return }
NSLog("--------Received message on 1: " + String(decoding: myData, as: UTF8.self))

            }
        }
    }))
    
}

The send function, is sending, but the connection1!.recieveMessage does not work, i'm not able to get a listneer work, for each connection, but the reciever in your class is working fine, but i can not detect which host the recieeMessage came from.

I'm think about using 3 classes class UDPListener: , and just rename them.

Sorry for the long message, but i hope it make sense.
All the best
Finn

@michael94ellis
Copy link
Author

Can you please correct the formatting? You may not be combining the calls correctly

@splons
Copy link

splons commented Jun 15, 2024

Thank you Michael, the UDP listener code worked great for a project I am considering that receives data from a piece of aviation hardware. Saved me a bunch of time sort all of this out on my own.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment