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()
}
}
@michael94ellis
Copy link
Author

michael94ellis commented Aug 4, 2023 via email

@Arttrm
Copy link

Arttrm commented Aug 19, 2023

Hi Michael,
I finally succeeded to make the UdpListener class to transfer data to the ContentView using the notification center. (I am not sure that this is the proper way to handle this but it seems to work...)
Many thanks again for your help through this project !

@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.

@michael94ellis
Copy link
Author

michael94ellis commented Aug 5, 2024 via email

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