From 2c634e7db692c323a76f74662286751054b94d3d Mon Sep 17 00:00:00 2001 From: Andrew Blakeslee Moore Date: Fri, 3 Jul 2026 01:41:42 -0700 Subject: [PATCH] QR Code Scan Failure Nucleic-Session: 80E30D7B-70A8-479A-A9A8-7E267486EAE1 Co-authored-by: Nucleic --- .../NucleicRemote/Models/RemoteStore.swift | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/NucleicRemote/NucleicRemote/Models/RemoteStore.swift b/NucleicRemote/NucleicRemote/Models/RemoteStore.swift index 329099f..0c2a579 100644 --- a/NucleicRemote/NucleicRemote/Models/RemoteStore.swift +++ b/NucleicRemote/NucleicRemote/Models/RemoteStore.swift @@ -695,7 +695,35 @@ final class RemoteStore: ObservableObject { } private func makeChannel(_ endpoint: NWEndpoint) -> NWFrameChannel { - NWFrameChannel(endpoint: endpoint) + let channel = NWFrameChannel(endpoint: endpoint) + // Surface a transport-level failure with an actionable message. Without this, a refused + // connection or a denied local-network permission finished the frame stream and only + // showed up as a generic "handshake closed" — hiding the real (usually fixable) cause. + channel.onFailed = { [weak self] error in + Task { @MainActor in self?.handleTransportFailure(error) } + } + return channel + } + + /// Map an `NWConnection` failure to a connectivity state the pairing/onboarding UI can act on. + /// Only meaningful while we're still establishing the link; once `.connected`, a drop is the + /// normal `.closed` → reconnect path's job. + private func handleTransportFailure(_ error: String) { + guard connectivity != .connected else { return } + connectivity = .failed(Self.friendlyTransportError(error)) + } + + /// Turn a raw `NWError` string into a short, fixable hint. The common onboarding failures are + /// a denied Local Network permission (EPERM / -65555) and the Mac not listening (refused). + private static func friendlyTransportError(_ error: String) -> String { + let lower = error.lowercased() + if lower.contains("denied") || lower.contains("not permitted") || lower.contains("65555") { + return "Can't reach the local network. In Settings ▸ Nucleic, allow Local Network access, then try again." + } + if lower.contains("refused") { + return "Your Mac refused the connection. Check that remote access is still on in Nucleic ▸ Settings." + } + return "Couldn't connect to your Mac — make sure it's on the same Wi‑Fi and remote access is on." } private func resolveEndpoint(fingerprint: String?, lanHost: String?, lanPort: UInt16?) -> NWEndpoint? {