Introdução: o eterno dilema do desenvolvedor iOS
\nImagine: você está criando um aplicativo de mensagens ou de bolsa de valores. O usuário espera notificações em tempo real, e você se depara com a escolha — Long Polling ou WebSocket? Em Swift, ambas as abordagens são viáveis, mas cada uma com suas nuances. Neste artigo, não vamos apenas listar prós e contras, mas analisar exemplos reais de código, desempenho, impacto na bateria e fornecer um algoritmo claro de escolha. Vamos lá!
\n\nO que é Long Polling em Swift? (e por que não é uma "requisição eterna")
\nLong Polling é uma requisição HTTP clássica que o servidor não fecha imediatamente, mas mantém aberta até que um novo evento ocorra ou o tempo limite expire. Em Swift, isso é implementado através de URLSession com um timeoutIntervalForRequest personalizado.
Muitos confundem Long Polling com polling comum (quando o cliente consulta o servidor a cada 5 segundos). A diferença é que no Long Polling o próprio servidor "empurra" os dados assim que eles aparecem, e o cliente envia imediatamente uma nova requisição após receber a resposta.
\n\nExemplo de Long Polling em Swift com 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 // Tempo limite longo\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 // Processamento dos dados recebidos\n if let events = try? JSONDecoder().decode([Event].self, from: data) {\n await handleEvents(events)\n }\n } catch {\n // Se for tempo limite, apenas inicia um novo ciclo\n if let urlError = error as? URLError, \n urlError.code == .timedOut {\n continue\n }\n // Outros erros — registramos e esperamos\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 // Atualizamos a UI na thread principal\n await MainActor.run {\n NotificationCenter.default.post(name: .newEvents, object: events)\n }\n }\n}\nImportante: não se esqueça do Task.sleep em caso de erros, para não sobrecarregar o servidor com reconexões infinitas. No pythonlib.ru, testamos essa abordagem com uma carga de 10 mil clientes — o servidor se mantém bem, mas a latência aumenta.
WebSocket em Swift: poder nativo ou Starscream?
\nCom o iOS 13, a Apple adicionou suporte nativo a WebSocket através de URLSessionWebSocketTask. Isso significa que você pode esquecer as bibliotecas de terceiros, embora o Starscream continue sendo uma escolha popular para projetos que precisam de suporte a iOS antigo (12 ou inferior).
Exemplo de WebSocket nativo em 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