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 <noreply@anthropic.com>
This commit is contained in:
Robin Kutesa
2026-06-21 02:06:01 +03:00
parent eec610024c
commit 760e6a121c

View File

@@ -60,13 +60,16 @@ final class WebSocketManager: ObservableObject {
} }
private func listen() { private func listen() {
task?.receive { [weak self] result in // Capture task locally so the Sendable receive closure doesn't reference
guard let self else { return } // the @MainActor-isolated property directly.
guard let t = task else { return }
t.receive { [weak self] result in
switch result { switch result {
case .failure: case .failure:
Task { @MainActor in self.handleDisconnect() } Task { @MainActor [weak self] in self?.handleDisconnect() }
case .success(let message): case .success(let message):
Task { @MainActor in Task { @MainActor [weak self] in
guard let self else { return }
self.handle(message: message) self.handle(message: message)
self.listen() self.listen()
} }
@@ -99,9 +102,13 @@ final class WebSocketManager: ObservableObject {
private func startPing() { private func startPing() {
pingTimer?.invalidate() pingTimer?.invalidate()
pingTimer = Timer.scheduledTimer(withTimeInterval: 30, repeats: true) { [weak self] _ in pingTimer = Timer.scheduledTimer(withTimeInterval: 30, repeats: true) { [weak self] _ in
self?.task?.sendPing { error in // Hop to MainActor before touching the isolated `task` property.
if error != nil { Task { @MainActor [weak self] in
Task { @MainActor in self?.handleDisconnect() } 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) let delay = min(pow(2.0, Double(attempt - 1)), maxBackoff)
connectionState = .reconnecting(attempt: attempt) connectionState = .reconnecting(attempt: attempt)
reconnectTask = Task { reconnectTask = Task { [weak self] in
try? await Task.sleep(nanoseconds: UInt64(delay * 1_000_000_000)) try? await Task.sleep(nanoseconds: UInt64(delay * 1_000_000_000))
guard !Task.isCancelled else { return } guard !Task.isCancelled else { return }
await MainActor.run { self.openConnection() } await MainActor.run { self?.openConnection() }
} }
} }
} }