Low-Level Networking on watchOS for Duplex audio streaming

Dear Apple Team,

I am facing an issue with UDP networking in my watchOS app for duplex audio streaming using NWConnection. I have already added the necessary capabilities, including background mode for audio, to ensure smooth operation.

Issue Details:

The UDP connection works fine on the simulator since it uses macOS networking and allows low-level access. However, on a real Apple Watch (running watchOS 10), the connection remains in a "waiting" state and fails with Error 50. I am aware of Technical Note TN3135 regarding low-level networking on watchOS, but even after following these guidelines, the issue persists.

Questions:

Does watchOS impose additional restrictions on UDP networking compared to iOS/macOS?

Are there any specific entitlements or configurations required to allow UDP connections on a real Apple Watch?

Is there a workaround or debugging method to get more insights into why the connection fails?

I would appreciate any guidance or recommendations on resolving this issue.

Answered by DTS Engineer in 830379022

Did you watch WWDC 2019 Session 716 Streaming Audio on watchOS 6? I explains how the low-level networking capability is tied to the presence of an active audio session:

Once your application has an active audio session, all of the networking APIs are available to retrieve audio content.

I suspect that you’ve failed to set that up, and so low-level networking is blocked.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Did you watch WWDC 2019 Session 716 Streaming Audio on watchOS 6? I explains how the low-level networking capability is tied to the presence of an active audio session:

Once your application has an active audio session, all of the networking APIs are available to retrieve audio content.

I suspect that you’ve failed to set that up, and so low-level networking is blocked.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

I did watch WWDC 2019 Session 716 and understand that an active audio session is key to unlocking low‑level networking on watchOS. I’m configuring my audio session and engine as follows:

private func configureAudioSession(completion: @escaping (Bool) -> Void) {
    let audioSession = AVAudioSession.sharedInstance()
    do {
        try audioSession.setCategory(.playAndRecord, mode: .voiceChat, options: [])
        try audioSession.setActive(true, options: .notifyOthersOnDeactivation)
        // Retrieve sample rate and configure the audio format.
        let sampleRate = audioSession.sampleRate
        print("Active hardware sample rate: \(sampleRate)")
        audioFormat = AVAudioFormat(standardFormatWithSampleRate: sampleRate, channels: 1)
        // Configure the audio engine.
        audioInputNode = audioEngine.inputNode
        audioEngine.attach(audioPlayerNode)
        audioEngine.connect(audioPlayerNode, to: audioEngine.mainMixerNode, format: audioFormat)
        
        try audioEngine.start()
        completion(true)
    } catch {
        print("Error configuring audio session: \(error.localizedDescription)")
        completion(false)
    }
}

private func setupUDPConnection() {
    let parameters = NWParameters.udp
    parameters.includePeerToPeer = true
    connection = NWConnection(host: "xxx.xxx.xxxxx.xxx", port: 0000, using: parameters)
    setupNWConnectionHandlers()
}

   
    private func setupTCPConnection() {
        let parameters = NWParameters.tcp
        connection = NWConnection(host: "xxx.xxx.xxxxx.xxx", port: 0000, using: parameters)
        setupNWConnectionHandlers()
    }

    
    private func setupWebSocketConnection() {
        guard let url = URL(string: "ws://xxx.xxx.xxxxx.xxx:0000") else {
            print("Invalid WebSocket URL")
            return
        }

        let session = URLSession(configuration: .default)
        webSocketTask = session.webSocketTask(with: url)
        webSocketTask?.resume()
        print("WebSocket connection initiated")
        sendAudioToServer()
        receiveDataFromServer()
        sendWebSocketPing(after: 0.6)
    }

    private func setupNWConnectionHandlers() {
        connection?.stateUpdateHandler = { [weak self] state in
            DispatchQueue.main.async {
                switch state {
                case .ready:
                    print("Connected (NWConnection)")
                    self?.isConnected = true
                    self?.failToConnect = false
                    self?.receiveDataFromServer()
                    self?.sendAudioToServer()
                case .waiting(let error), .failed(let error):

                    print("Connection error: \(error.localizedDescription)")
                    DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
                        self?.setupNetwork()
                    }
                case .cancelled:
                    print("NWConnection cancelled")
                    self?.isConnected = false

                default:
                    break
                }
            }
        }
        connection?.start(queue: .main)
    }

I’m using the .playAndRecord category with .voiceChat mode because my app requires duplex streaming—capturing audio to send to a server and playing back the incoming audio. Despite this setup and ensuring that the audio engine is running, my low-level connections (UDP, TCP, and WebSocket via NWConnection/URLSessionWebSocketTask) remain in a “waiting” state and eventually fail with Error 50 on a real Apple Watch.

I'm not using the simple activation snippet with an empty options array because it ask for audio route but i wanted to perform duplex audio streaming with default speaker and mic.

I am reaching out to seek further assistance regarding the challenges I've been experiencing with establishing a UDP, TCP & web socket connection on watchOS using NWConnection for duplex audio streaming. Despite implementing the recommendations provided earlier, I am still encountering difficulties. Or duplex audio streaming not possible on apple watch?

Low-Level Networking on watchOS for Duplex audio streaming
 
 
Q