You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI集成SignalR时WebSocket协商成功后20秒内断开(错误码1000)

问题根因

连接在建立后20秒内自动断开、返回1000状态码且无错误信息,核心疏漏是HubConnection的持有者生命周期过短,被系统自动回收:

  • SwiftUI的View是结构体值类型,你在ContentView.onAppear回调里局部实例化的SignalRController属于临时对象,onAppear闭包执行结束后没有任何强引用持有该实例,ARC会在短时间内将其回收,关联的HubConnection对象随之销毁,触发WebSocket正常关闭(1000就是WebSocket正常关闭的标准状态码,不会附带错误原因)。
  • 相同逻辑在UIKit中可正常运行,是因为UIKit场景下你大概率将SignalRController(本身为UIViewController子类)加入了控制器层级,或是被当前页面的强属性引用,生命周期和页面绑定,不会被提前回收。
修复方案

无需使用UIViewController子类包装SignalR逻辑,直接通过SwiftUI的状态持有对象管理连接生命周期,推荐采用ObservableObject实现连接管理者,被View的状态属性强持有,保证生命周期和页面一致。

标准实现示例

1. 定义SignalR连接管理者

import SwiftUI
import SignalRClient

class SignalRManager: ObservableObject, HubConnectionDelegate {
    private var hubConnection: HubConnection?
    // 对外暴露可绑定的状态,供View层直接使用
    @Published var connectionStatus: String = "未连接"
    @Published var receivedMessages: [String] = []
    
    init() {
        setupConnection()
    }
    
    private func setupConnection() {
        // 替换为实际的SignalR服务端地址
        guard let url = URL(string: "你的SignalR服务端地址") else { return }
        hubConnection = HubConnectionBuilder(url: url)
            .withAutoReconnect() // 可根据业务需求选择是否开启自动重连
            .build()
        hubConnection?.delegate = self
        
        // 注册客户端接收方法,逻辑与你原有实现保持一致即可
        hubConnection?.on(method: "ReceiveMessage") { (user: String, message: String) in
            DispatchQueue.main.async {
                self.receivedMessages.append("\(user): \(message)")
            }
        }
    }
    
    // 启动连接
    func start() {
        guard hubConnection?.state != .connected else { return }
        hubConnection?.start()
    }
    
    // 停止连接,页面销毁时按需调用
    func stop() {
        hubConnection?.stop()
    }
    
    // 发送消息示例
    func sendMessage(user: String, content: String) {
        hubConnection?.invoke(method: "SendMessage", user, content) { error in
            if let error = error {
                print("消息发送失败: \(error.localizedDescription)")
            }
        }
    }
    
    // MARK: - HubConnectionDelegate 回调实现
    func connectionDidOpen(hubConnection: HubConnection) {
        DispatchQueue.main.async {
            self.connectionStatus = "已连接"
        }
    }
    
    func connectionDidFailToOpen(error: Error) {
        DispatchQueue.main.async {
            self.connectionStatus = "连接失败: \(error.localizedDescription)"
        }
    }
    
    func connectionDidClose(error: Error?) {
        DispatchQueue.main.async {
            if let error = error {
                self.connectionStatus = "连接异常断开: \(error.localizedDescription)"
            } else {
                self.connectionStatus = "连接已关闭"
            }
        }
    }
}

2. 在SwiftUI视图中接入

struct ContentView: View {
    // 用@StateObject强持有连接管理者,生命周期与View绑定,不会被提前回收
    @StateObject private var signalRManager = SignalRManager()
    
    var body: some View {
        VStack {
            Text("当前连接状态: \(signalRManager.connectionStatus)")
                .padding()
            List(signalRManager.receivedMessages, id: \.self) { message in
                Text(message)
            }
        }
        .onAppear {
            // 页面展示时启动连接
            signalRManager.start()
        }
        .onDisappear {
            // 若需要全局保持连接,可将停止逻辑移至应用生命周期回调中处理
            signalRManager.stop()
        }
    }
}
注意事项
  • 如果需要SignalR连接全局存活、不随页面销毁断开,可将SignalRManager实例放在App根视图用@StateObject持有,或注入到SwiftUI环境中供全应用复用,禁止在页面的onAppear回调中局部创建连接对象。
  • 非必要不要为了适配SwiftUI包装UIViewController子类承载业务逻辑,若确实需要使用UIKit类型的封装,必须通过UIViewControllerRepresentable做标准桥接,正确管理控制器生命周期,避免出现临时对象被回收的问题。
  • 调试时如果遇到WebSocket 1000关闭码,优先排查连接对象是否被提前释放、是否主动调用了stop方法,无需优先排查服务端配置问题。

内容的提问来源于stack exchange,提问作者J.Chandrasekhar Reddy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 18:18:39