Introduction: The Eternal Dilemma of an iOS Developer
\nImagine: you are writing a messenger or a stock trading app. The user expects real-time notifications, and you are faced with a choice — Long Polling or WebSocket? In Swift, both approaches have a right to exist, but each comes with its own nuances. In this article, we will not just list the pros and cons, but will analyze real code examples, performance, impact on battery life, and provide a clear decision-making algorithm. Let's go!
\n\nWhat is Long Polling in Swift? (and why it's not an "eternal request")
\nLong Polling is a classic HTTP request that the server does not close immediately, but keeps open until a new event occurs or a timeout expires. In Swift, this is implemented via URLSession with a custom timeoutIntervalForRequest.
Many people confuse Long Polling with regular polling (where the client pings the server every 5 seconds). The difference is that with Long Polling, the server itself "pushes" data as soon as it appears, and the client immediately sends a new request after receiving the response.
\n\nLong Polling Example in Swift with async/await
\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 // Long timeout\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 // Process received data\n if let events = try? JSONDecoder().decode([Event].self, from: data) {\n await handleEvents(events)\n }\n } catch {\n // If timeout — just start a new cycle\n if let urlError = error as? URLError, \n urlError.code == .timedOut {\n continue\n }\n // Other errors — log and wait\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 // Update UI on the main thread\n await MainActor.run {\n NotificationCenter.default.post(name: .newEvents, object: events)\n }\n }\n}\nImportant: don't forget about Task.sleep on errors to avoid spamming the server with endless reconnects. At pythonlib.ru, we tested this approach under a load of 10k clients — the server holds up well, but latency increases.
WebSocket in Swift: Native Power or Starscream?
\nSince iOS 13, Apple has added native WebSocket support via URLSessionWebSocketTask. This means you can forget about third-party libraries, although Starscream remains a popular choice for projects that need support for older iOS versions (12 and below).
Native WebSocket Example in Swift (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