watchOS: Network framework WebSocket loses its path ~35 s in, while URLSession keeps working

Following up on TN3135 and the resolution in https://developer.apple.com/forums/thread/773362 — that thread solved establishing a low-level connection on watchOS (the asynchronous AVAudioSession.activate(options:completionHandler:) instead of the synchronous setActive()). This question is about a connection staying established, which I could not find discussed anywhere.

Environment: Apple Watch, watchOS 26.6 (23U67). Audio app, WKBackgroundModes = ["self-care"]. Real device, TestFlight build, not the simulator.

What works

Opening an NWConnection WebSocket to my own server is reliable — 8 attempts out of 8 reached .ready in 0.28–0.98 s, and an echo frame round-tripped in 27–89 ms.

Interestingly, in my measurements it opens under BOTH activation variants: the asynchronous activate(options:completionHandler:) AND the synchronous setActive(true). The two are within ~0.2 s of each other. I mention it only because the thread above concluded the synchronous one is insufficient; on 26.6 I cannot reproduce that difference for establishment.

What fails

The connection goes quiet after roughly half a minute, and an NWPathMonitor running alongside it shows why: the path transitions to .unsatisfied. Four runs:

+34.0 s (cellular) +34.6 s (cellular) +36.0 s (Wi-Fi) +34.3 s (companion link only — availableInterfaces ["other", "other"])

The server sends a heartbeat frame every 5 s and closes the socket on a schedule, so I can tell "the peer closed" from "we stopped receiving". The client receives beats 1–6 (5 s … 30 s) and then nothing; the scheduled close never arrives.

What I ruled out

  • Server side. The same client construction run on macOS against the same endpoint receives all 8 heartbeats and the scheduled close at 45.1 s.
  • Both audio-session activation variants — no difference, as above.
  • Network type — cellular, Wi-Fi and companion-link-only all drop at ~35 s.
  • The app being suspended. The app keeps logging densely throughout, and in the last run it held a WKExtendedRuntimeSession (delegate reported extendedRuntimeSessionDidStart) and was actively playing audio through AVAudioEngine from the first second — i.e. the audio-streaming condition TN3135 describes — for the entire window. The path dropped anyway, at +34.3 s.
  • An idle socket. Server traffic arrives every 5 s until the drop.

The comparison that puzzles me

The same app, on the same watch, the same afternoon, relays the same realtime audio session over plain HTTPS (URLSession) instead — and that runs for 64 s continuously without a stall, including straight through a WatchConnectivity "reachability settled: unreachable" transition.

So a high-level URLSession request stream survives a period in which a low-level NWConnection's path is reported unsatisfied. That is consistent with the note in thread 773362 that "on watchOS every session is kinda like a background session, where the actual work is done out of process" — but it leaves me unsure what the intended behaviour is.

Questions

  1. Is a ~35 s path lifetime the expected behaviour for low-level networking on watchOS, or does it indicate something wrong on my side?
  2. Does the TN3135 audio-streaming exception cover only the establishment of a low-level connection, or is it also supposed to keep the path available for the duration of the audio streaming?
  3. If it is supposed to persist: is there something beyond an active audio session, flowing audio and a WKExtendedRuntimeSession that an app must do to keep the path alive?
  4. If ~35 s is the expected ceiling, is a WebSocket a supported transport for a multi-minute conversational audio session on watchOS at all — or is relaying over URLSession the intended approach despite the guidance to prefer Network framework?

Happy to file a bug with a sysdiagnose and a reduced sample project if that is more useful — please say the word and I will attach the numbers above.

watchOS: Network framework WebSocket loses its path ~35 s in, while URLSession keeps working
 
 
Q