Sua API sob ataque: por que rate limiting e throttling são must have para desenvolvedores Swift
\n\nImagine: você lançou um aplicativo incrível em Swift, os usuários estão encantados, os servidores funcionam perfeitamente. Mas de repente — colapso. Alguém executou um script que sobrecarrega sua API com milhares de requisições por segundo. Ou apenas um usuário ativo fica spamando o botão "Atualizar" mais rápido do que você consegue processar os dados. Resultado — lentidão, queda do servidor e avaliações negativas.
\n\nFamiliar? Então este artigo é para você. Vamos abordar rate limiting e throttling em Swift — técnicas que salvarão seu backend e tornarão o aplicativo estável. Você aprenderá como implementar a limitação de requisições em Swift no lado do cliente, usando abordagens modernas com async/await, e quais padrões realmente funcionam.
\n\nRate limiting vs Throttling: qual a diferença?
\n\nAntes de escrever código, vamos esclarecer os termos. Muitos confundem esses conceitos, embora eles resolvam problemas diferentes.
\n\nRate limiting (limitação de velocidade)
\nÉ um limite rígido: "No máximo N requisições por período". Se o limite for excedido — a requisição é rejeitada. O servidor geralmente retorna 429 Too Many Requests. Exemplo: você permite no máximo 100 requisições por minuto de um único IP.
Throttling (estrangulamento)
\nÉ um mecanismo mais suave: as requisições não são bloqueadas, mas colocadas em uma fila e executadas em uma frequência definida. Imagine que sua API pode processar apenas 5 requisições por segundo. O throttling as colocará em fila e as enviará na velocidade necessária, sem sobrecarregar o servidor.
\n\nEm Swift, ambas as abordagens são implementadas de forma diferente. Vamos analisar as melhores práticas.
\n\nImplementações práticas de rate limiting em Swift
\n\n1. Throttling simples com timer (GCD)
\n\nA maneira mais simples de limitar requisições em Swift é usar DispatchQueue e um timer. Aqui está um exemplo que garante que as requisições sejam enviadas no máximo uma vez por segundo:
\n\nimport Foundation\n\nfinal class RequestThrottler {\n private let queue = DispatchQueue(label: "com.app.throttle")\n private var lastRequestTime: Date = .distantPast\n private let minimumInterval: TimeInterval\n \n init(interval: TimeInterval = 1.0) {\n self.minimumInterval = interval\n }\n \n func performThrottledRequest(block: @escaping () -> Void) {\n queue.sync {\n let now = Date()\n if now.timeIntervalSince(lastRequestTime) >= minimumInterval {\n lastRequestTime = now\n DispatchQueue.main.async {\n block()\n }\n } else {\n print("Requisição rejeitada: muito frequente")\n }\n }\n }\n}\n\n// Uso\nlet throttler = RequestThrottler(interval: 2.0)\nthrottler.performThrottledRequest {\n // Sua requisição de API\n print("Requisição executada")\n}\n\nEssa abordagem é simples, mas não é ideal: ela simplesmente descarta requisições "excedentes". Para muitos cenários, isso é suficiente, mas se você precisar preservar a ordem e garantir a execução — continue lendo.
\n\n2. Rate limiter com fila (async/await)
\n\nCom o advento do Swift async/await, ganhamos uma maneira elegante de implementar um swift rate limiter com suporte a fila. Veja como fica:
\n\nimport Foundation\n\nactor RateLimiter {\n private let maxRequests: Int\n private let perSecond: Double\n private var requestTimestamps: [Date] = []\n private var pendingTasks: [CheckedContinuation] = []\n private var isProcessing = false\n \n init(maxRequests: Int, perSecond: Double) {\n self.maxRequests = maxRequests\n self.perSecond = perSecond\n }\n \n func waitForSlot() async throws {\n try await withCheckedThrowingContinuation { continuation in\n pendingTasks.append(continuation)\n if