引言:iOS 开发者的永恒难题
\n想象一下:您正在开发一个即时通讯应用或股票交易应用。用户期待实时通知,而您面临一个选择——Long Polling 还是 WebSocket?在 Swift 中,这两种方法都有其适用场景,但各有其细微差别。本文将不仅列举优缺点,还将深入分析真实的代码示例、性能表现、对电池的影响,并给出清晰的选择策略。让我们开始吧!
\n\nSwift 中的 Long Polling 是什么?(为什么它不是“永久的请求”)
\nLong Polling 是一种经典的 HTTP 请求,服务器不会立即关闭连接,而是保持打开状态,直到有新事件发生或超时。在 Swift 中,这可以通过 URLSession 配合自定义的 timeoutIntervalForRequest 来实现。
许多人将 Long Polling 与普通的轮询(客户端每 5 秒请求一次服务器)混淆。区别在于,Long Polling 中服务器会在数据可用时主动“推送”数据,而客户端在收到响应后会立即发送新的请求。
\n\n使用 async/await 的 Swift Long Polling 示例
\nimport Foundation\n\nclass LongPollingClient {\n private let session: URLSession\n private let baseURL = URL(string: "https://api.pythonlib.ru/events")!\n private var isPolling = false\n \n init() {\n let config = URLSessionConfiguration.default\n config.timeoutIntervalForRequest = 60 // 长超时时间\n config.waitsForConnectivity = true\n self.session = URLSession(configuration: config)\n }\n \n func startPolling() async {\n isPolling = true\n while isPolling {\n do {\n let (data, response) = try await session.data(from: baseURL)\n guard let httpResponse = response as? HTTPURLResponse,\n httpResponse.statusCode == 200 else {\n continue\n }\n // 处理接收到的数据\n if let events = try? JSONDecoder().decode([Event].self, from: data) {\n await handleEvents(events)\n }\n } catch {\n // 如果超时,只需开始新的循环\n if let urlError = error as? URLError, \n urlError.code == .timedOut {\n continue\n }\n // 其他错误——记录日志并等待\n print("Polling error: \\(error.localizedDescription)")\n try? await Task.sleep(nanoseconds: 5_000_000_000)\n }\n }\n }\n \n func stopPolling() {\n isPolling = false\n }\n \n private func handleEvents(_ events: [Event]) async {\n // 在主线程更新 UI\n await MainActor.run {\n NotificationCenter.default.post(name: .newEvents, object: events)\n }\n }\n}\n重要提示: 在出现错误时不要忘记使用 Task.sleep,以避免无限重连导致服务器过载。在 pythonlib.ru 上,我们测试了这种方案在 10k 客户端负载下的表现——服务器表现良好,但延迟会有所增加。
Swift 中的 WebSocket:原生能力还是 Starscream?
\n从 iOS 13 开始,Apple 通过 URLSessionWebSocketTask 提供了对 WebSocket 的原生支持。这意味着您可以不再依赖第三方库,尽管 Starscream 仍然是需要支持旧版 iOS(12 及以下)项目的热门选择。
Swift 原生 WebSocket 示例(iOS 13+)
\nimport Foundation\n\nclass WebSocketManager: NSObject, URLSessionWebSocketDelegate {\n private var webSocket: URLSessionWebSocketTask?\n private let session: URLSession\n \n override init() {\n let config = URLSessionConfiguration.default\n config.timeoutIntervalForRequest = 60\n self.session = URLSession(configuration: config)\n