Swift中App进入后台时如何保持WebSocket连接?
iOS后台维持URLSessionWebSocketTask连接的可行方案
1. 改用后台URLSession配置
你当前使用的.default会话配置在App进入后台后会被系统挂起,需要替换为后台专用配置,确保会话能在后台继续运行:
private var websocket: URLSessionWebSocketTask? private var urlSession: URLSession? // 保留会话引用,避免被提前释放 func connectWebsocket() { // 创建带唯一标识符的后台会话配置,用于系统唤醒App时识别 let config = URLSessionConfiguration.background(withIdentifier: "com.your.app.websocket.background") config.sessionSendsLaunchEvents = true // 可选:会话有事件时唤醒App config.isDiscretionary = false // 立即执行任务,不等待系统空闲 urlSession = URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue()) guard let url = URL(string: "ws://URL...") else { return } websocket = urlSession?.webSocketTask(with: url) websocket?.resume() }
2. 申请后台任务断言延长活跃时间
App进入后台后,系统会快速挂起进程,需要申请后台任务断言获得额外运行时间,用来发送心跳包维持连接:
private var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid func sceneDidEnterBackground(_ scene: UIScene) { // 申请后台任务,超时后自动结束 backgroundTaskID = UIApplication.shared.beginBackgroundTask(withName: "WebSocketKeepAlive") { [weak self] in UIApplication.shared.endBackgroundTask(self?.backgroundTaskID ?? .invalid) self?.backgroundTaskID = .invalid } // 启动心跳定时器,建议30-60秒发送一次ping包 startHeartbeatTimer() } private func startHeartbeatTimer() { Timer.scheduledTimer(withTimeInterval: 30, repeats: true) { [weak self] timer in guard let websocket = self?.websocket else { timer.invalidate() return } websocket.send(.ping(Data())) { error in if let error = error { print("Ping发送失败: \(error.localizedDescription)") // 此处可触发重连逻辑 } } } }
3. 适配苹果后台限制
- 后台任务断言有效时间有限(通常几分钟),若需长期保持连接,建议结合静默远程通知:服务器有消息时发送静默通知唤醒App,此时可重新建立连接或处理消息。
- 优先使用
wss://加密协议,明文ws://连接更容易被系统在后台断开。 - 连接断开时避免频繁重连,可加入指数退避机制(如10秒、20秒、40秒递增),防止被系统判定为滥用。
4. 代理回调处理连接异常
实现URLSessionWebSocketDelegate的断开回调,及时处理重连:
func webSocketTask(_ task: URLSessionWebSocketTask, didCloseWith closeCode: URLSessionWebSocketTask.CloseCode, reason: Data?) { print("WebSocket连接断开: \(closeCode.rawValue)") // 后台状态下尝试重连(需确保有后台任务时间) if UIApplication.shared.applicationState == .background { if backgroundTaskID != .invalid { connectWebsocket() } } else { connectWebsocket() } }
内容的提问来源于stack exchange,提问作者msgu
相关产品推荐
相关产品推荐

