NwListenerState

sealed interface NwListenerState

The current lifecycle state of this peer's inbound listener — the advertise+accept half of the full mesh, started by NwApi.startListening and reported as latest-value STATE by NwApi.listenerState (#2449).

Why this exists at all

NwApi.startListening is suspend fun … : Unit, and Network.framework decides whether the bind succeeded after it returns, on a GCD callback. So a listener that never comes up — or one that was up and then died while the app was suspended — produced no signal any caller could observe: the runCatchingCancellable around startListening sees only a synchronous throw, and there is none. The 2026-08-17 field capture is exactly that shape: both peers logged a listener failure minutes into a game and neither ever listened again, because there was nothing to retry on. This flow is the missing signal; NwLoom watches it and re-listens with back-off.

Not a connection state

This is one state for the whole listener, not per-connection — NwApi.connectionStates covers the links. A Failed listener does not tear existing connections: already-established peers keep working, the peer simply stops being reachable inbound, which is why the failure is otherwise silent.

Inheritors

Types

Link copied to clipboard
data class Failed(val domain: Int, val code: Int) : NwListenerState

The listener terminally failed, carrying the decoded nw_error_t the OS handed the state-changed handler. domain is the raw nw_error_domain_t (invalid=0 / posix=1 / dns=2 / tls=3) and code is the domain-specific code — a POSIX errno for domain 1, a DNSServiceErrorType for domain 2, a TLS alert/OSStatus for domain 3. The SAME vocabulary the connection path has reported since #1560, deliberately, so a listener failure and a link failure read alike in one capture.

Link copied to clipboard
data object Ready : NwListenerState

The listener is bound and advertising — inbound connections can be accepted.

Link copied to clipboard

A listener has been created and started, and the OS has not yet reported a verdict. Published by NwApi.startListening itself, before the new listener can call back, so a watcher never reads the PREVIOUS listener's terminal Failed as this attempt's verdict.

Link copied to clipboard
data object Unknown : NwListenerState

No listener signal — the default a binding inherits when it has not wired the underlying state-changed handler, and the state before NwApi.startListening is first called. Says nothing about whether a listener is up: never infer a failure from it (a binding that never updates this flow sits here forever, which is the pre-#2449 behaviour and is deliberately non-actionable).

Link copied to clipboard
data class Waiting(val domain: Int, val code: Int) : NwListenerState

The OS has the listener but cannot yet receive connections — nw_listener_state_waiting, documented as "waiting for a usable network before being able to receive connections". domain/code carry the decoded nw_error_t explaining what it is waiting on, in the same vocabulary as Failed.