How to work with UDP sockets in iOS, swift?

11,758

Solution 1

This solution work for me! Thanks @Paulw11
Swift 4, XCode 10.1, iOS 12.0
Simple connect to the public UDP server (This is NOT optimal version but works):

import UIKit
import Network

class ViewController: UIViewController {

    var connection: NWConnection?
    var hostUDP: NWEndpoint.Host = "iperf.volia.net"
    var portUDP: NWEndpoint.Port = 5201

    override func viewDidLoad() {
        super.viewDidLoad()

        // Hack to wait until everything is set up
        var x = 0
        while(x<1000000000) {
            x+=1
        }
        connectToUDP(hostUDP,portUDP)
    }

    func connectToUDP(_ hostUDP: NWEndpoint.Host, _ portUDP: NWEndpoint.Port) {
        // Transmited message:
        let messageToUDP = "Test message"

        self.connection = NWConnection(host: hostUDP, port: portUDP, using: .udp)

        self.connection?.stateUpdateHandler = { (newState) in
            print("This is stateUpdateHandler:")
            switch (newState) {
                case .ready:
                    print("State: Ready\n")
                    self.sendUDP(messageToUDP)
                    self.receiveUDP()
                case .setup:
                    print("State: Setup\n")
                case .cancelled:
                    print("State: Cancelled\n")
                case .preparing:
                    print("State: Preparing\n")
                default:
                    print("ERROR! State not defined!\n")
            }
        }

        self.connection?.start(queue: .global())
    }

    func sendUDP(_ content: Data) {
        self.connection?.send(content: content, completion: NWConnection.SendCompletion.contentProcessed(({ (NWError) in
            if (NWError == nil) {
                print("Data was sent to UDP")
            } else {
                print("ERROR! Error when data (Type: Data) sending. NWError: \n \(NWError!)")
            }
        })))
    }

    func sendUDP(_ content: String) {
        let contentToSendUDP = content.data(using: String.Encoding.utf8)
        self.connection?.send(content: contentToSendUDP, completion: NWConnection.SendCompletion.contentProcessed(({ (NWError) in
            if (NWError == nil) {
                print("Data was sent to UDP")
            } else {
                print("ERROR! Error when data (Type: Data) sending. NWError: \n \(NWError!)")
            }
        })))
    }

    func receiveUDP() {
        self.connection?.receiveMessage { (data, context, isComplete, error) in
            if (isComplete) {
                print("Receive is complete")
                if (data != nil) {
                    let backToString = String(decoding: data!, as: UTF8.self)
                    print("Received message: \(backToString)")
                } else {
                    print("Data == nil")
                }
            }
        }
    }
}

Solution 2

You need to wait until your connection is in the ready state before you try and send or receive any data. You will also need to hold a strong reference to your connection in a property to prevent it from being released as soon as the function exits.

var connection: NWConnection?

func someFunc() {

    self.connection = NWConnection(host: "255.255.255.255", port: 9093, using: .udp)

    self.connection?.stateUpdateHandler = { (newState) in
        switch (newState) {
        case .ready:
            print("ready")
            self.send()
            self.receive()
        case .setup:
            print("setup")
        case .cancelled:
            print("cancelled")
        case .preparing:
            print("Preparing")
        default:
            print("waiting or failed")

        }
    }
    self.connection?.start(queue: .global())

}

func send() {
    self.connection?.send(content: "Test message".data(using: String.Encoding.utf8), completion: NWConnection.SendCompletion.contentProcessed(({ (NWError) in
        print(NWError)
    })))
}

func receive() {
    self.connection?.receiveMessage { (data, context, isComplete, error) in
        print("Got it")
        print(data)
    }
}
Share:
11,758
SBlincov
Author by

SBlincov

Winner of international and Russian olympiads, contests and hackathons: Since my school days, I have become the winner of many International, Russian and regional hackathons, contests and olympiads in computer science, economics and programming. Skoltech Master's student: I am studying at the Skoltech University Master's program in Information Science and Technology. IoT track. Graduated from the Institute of Information Technologies, Mathematics and Mechanics of the Lobachevsky University (UNN) in the direction of "Software Engineering". Graduated from the program "Innovative Entrepreneurship" FPK UNN. iOS developer: I am developing mobile applications for the iOS operating system. I have experience as team and as independent developer. Developed mobile applications for banks, startups, UAV manufacturer, government, Russian and foreign customers. Technology entrepreneur: I take an active part in the development of my own projects: "meCare", "BeDance" and hardware and software complex for the development of communication skills of people with ASD. My personal web-site: SBlincov.ru

Updated on July 22, 2022

Comments

  • SBlincov
    SBlincov almost 2 years

    I'm trying connect to local esp8266 UDP server. SwiftSocket haven't documentation. CocoaAsyncSocket doesn't work. How to connect and send data to udp server? What i should do?

    I wrote sample UDP python server and tried connect to them via SwiftSocket and CocoaAsyncSocket. I'm don't get feedback from app. Server don't receive connections. For example- one of the most attempts:

        var connection = NWConnection(host: "255.255.255.255", port: 9093, using: .udp)
    
        connection.stateUpdateHandler = { (newState) in
            switch (newState) {
            case .ready:
                print("ready")
            case .setup:
                print("setup")
            case .cancelled:
                print("cancelled")
            case .preparing:
                print("Preparing")
            default:
                print("waiting or failed")
                break
            }
        }
        connection.start(queue: .global())
        connection.send(content: "Xyu".data(using: String.Encoding.utf8), completion: NWConnection.SendCompletion.contentProcessed(({ (NWError) in
            print(NWError)
        })))
        connection.receiveMessage { (data, context, isComplete, error) in
            print("Got it")
            print(data)
        }
    

    Can't connect to the server

  • SBlincov
    SBlincov over 5 years
    Thank you for answer! When i'm run this code on empty project (run someFunction() from viewDidLoad() ) i get this error:
  • SBlincov
    SBlincov over 5 years
    2019-02-12 22:28:41.553015+0300 Nanopix Pilot[3891:189405] [] nw_socket_service_writes_block_invoke [C1:1] sendmsg(fd 5, 3 bytes) [13: Permission denied]
  • Paulw11
    Paulw11 over 5 years
    You are trying to send a broadcast packet; I am not sure if this is permitted. It would be better to send a packet to the specific host you are trying to communicate with
  • SBlincov
    SBlincov over 5 years
    Thank you so much! I successfully connected to public UDP server. Below i write final code
  • user3069232
    user3069232 over 4 years
    Trying to get this to work on swift 5.x iOS 13. Sends data no problem, but I cannot seem to get it to receive anything. I am trying to use a python script to send a simple message.
  • Flo
    Flo over 4 years
    You saved my life ! Finally I was able to open a connection and to send a message to the server using Swift 5 and without to upgrade my macOS to Catalina ! Thank you ! P.S.: If you need to connect using TCP is also working this example, just change from UDP to TCP.
  • Ramcharan Reddy
    Ramcharan Reddy about 3 years
    how to make the connection to listen for continuously?