From 760e6a121ccf16854621bed953a39b49e35348a1 Mon Sep 17 00:00:00 2001 From: Robin Kutesa Date: Sun, 21 Jun 2026 02:06:01 +0300 Subject: [PATCH] fix: resolve Swift 6 concurrency warnings in WebSocketManager - listen(): capture `task` as local `t` before the Sendable receive closure so the @MainActor-isolated property isn't referenced inside a Sendable context - startPing(): dispatch to @MainActor inside the timer block before accessing `task` - scheduleReconnect(): capture `self` weakly in the reconnect Task XcodeGen regenerated to include BackgroundRefreshManager.swift in the project. Co-Authored-By: Claude Sonnet 4.6 --- Jarvis/Networking/WebSocketManager.swift | 25 +++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/Jarvis/Networking/WebSocketManager.swift b/Jarvis/Networking/WebSocketManager.swift index 242a415..7560c4d 100644 --- a/Jarvis/Networking/WebSocketManager.swift +++ b/Jarvis/Networking/WebSocketManager.swift @@ -60,13 +60,16 @@ final class WebSocketManager: ObservableObject { } private func listen() { - task?.receive { [weak self] result in - guard let self else { return } + // Capture task locally so the Sendable receive closure doesn't reference + // the @MainActor-isolated property directly. + guard let t = task else { return } + t.receive { [weak self] result in switch result { case .failure: - Task { @MainActor in self.handleDisconnect() } + Task { @MainActor [weak self] in self?.handleDisconnect() } case .success(let message): - Task { @MainActor in + Task { @MainActor [weak self] in + guard let self else { return } self.handle(message: message) self.listen() } @@ -99,9 +102,13 @@ final class WebSocketManager: ObservableObject { private func startPing() { pingTimer?.invalidate() pingTimer = Timer.scheduledTimer(withTimeInterval: 30, repeats: true) { [weak self] _ in - self?.task?.sendPing { error in - if error != nil { - Task { @MainActor in self?.handleDisconnect() } + // Hop to MainActor before touching the isolated `task` property. + Task { @MainActor [weak self] in + guard let self else { return } + self.task?.sendPing { [weak self] error in + if error != nil { + Task { @MainActor [weak self] in self?.handleDisconnect() } + } } } } @@ -119,10 +126,10 @@ final class WebSocketManager: ObservableObject { let delay = min(pow(2.0, Double(attempt - 1)), maxBackoff) connectionState = .reconnecting(attempt: attempt) - reconnectTask = Task { + reconnectTask = Task { [weak self] in try? await Task.sleep(nanoseconds: UInt64(delay * 1_000_000_000)) guard !Task.isCancelled else { return } - await MainActor.run { self.openConnection() } + await MainActor.run { self?.openConnection() } } } }