Rate Limiting en Swift: Limitación de frecuencia de solicitudes para iOS y macOS

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Rate Limiting en Swift: Guía completa para aplicaciones iOS y macOS

\n\n

Rate limiting (limitación de frecuencia de solicitudes) es un mecanismo críticamente importante para el funcionamiento estable de aplicaciones móviles y de escritorio. En el ecosistema Swift (iOS/macOS), este enfoque permite controlar la cantidad de llamadas a API, operaciones de entrada/salida o cualquier acción repetitiva por unidad de tiempo. Sin rate limiting, su aplicación corre el riesgo de ser bloqueada por el servidor, agotar la batería con reintentos infinitos o crear una carga excesiva en la red.

\n\n

¿Por qué es necesaria la limitación de frecuencia de solicitudes en Swift?

\n\n
    \n
  • Protección contra la superación de límites de API — muchos servidores (por ejemplo, GitHub, Twitter, Stripe) establecen cuotas estrictas sobre la cantidad de solicitudes por minuto/hora.
  • \n
  • Optimización del rendimiento — prevención de una "tormenta" de llamadas durante el desplazamiento rápido o animaciones.
  • \n
  • Ahorro de tráfico y batería — especialmente relevante para dispositivos móviles.
  • \n
  • Cumplimiento de las políticas de App Store — las solicitudes en segundo plano excesivas pueden provocar el rechazo de la aplicación.
  • \n
\n\n

Enfoques principales para implementar Rate Limiting en Swift

\n\n

Consideremos tres patrones básicos que se pueden implementar sin bibliotecas externas:

\n\n

1. Token Bucket (Cubo de fichas)

\n

Algoritmo clásico donde las "fichas" se agregan a una velocidad fija. Cada solicitud consume una ficha. Si no hay fichas, la solicitud es rechazada.

\n\n
import Foundation\n\nfinal class TokenBucket {\n    private let capacity: Int\n    private var tokens: Int\n    private let refillRate: Double // fichas por segundo\n    private var lastRefill: Date\n    private let queue = DispatchQueue(label: "token.bucket.queue")\n    \n    init(capacity: Int, refillRate: Double) {\n        self.capacity = capacity\n        self.tokens = capacity\n        self.refillRate = refillRate\n        self.lastRefill = Date()\n    }\n    \n    func tryConsume() -> Bool {\n        queue.sync {\n            refill()\n            if tokens > 0 {\n                tokens -= 1\n                return true\n            }\n            return false\n        }\n    }\n    \n    private func refill() {\n        let now = Date()\n        let elapsed = now.timeIntervalSince(lastRefill)\n        let newTokens = Int(elapsed * refillRate)\n        if newTokens > 0 {\n            tokens = min(capacity, tokens + newTokens)\n            lastRefill = now\n        }\n    }\n}\n\n// Ejemplo de uso\nlet limiter = TokenBucket(capacity: 10, refillRate: 2.0) // 10 solicitudes, 2 por segundo\nif limiter.tryConsume() {\n    print("✅ Solicitud permitida")\n} else {\n    print("⛔️ Límite excedido, espere")\n}
\n\n

2. Algoritmo de ventana deslizante (Sliding Window)

\n

Almacena las marcas de tiempo de las últimas N solicitudes exitosas. Si la ventana está llena, bloquea nuevas llamadas.

\n\n
final class SlidingWindowLimiter {\n    private let maxRequests: Int\n    private let windowDuration: TimeInterval\n    private var timestamps: [Date] = []\n    private let queue = DispatchQueue(label: "sliding.window.queue")\n    \n    init(maxRequests: Int, windowDuration: TimeInterval = 60) {\n        self.maxRequests = maxRequests\n        self.windowDuration = windowDuration\n    }\n    \n    func canProceed() -> Bool {\n        queue.sync {\n            let now = Date()\n            // Eliminar marcas obsoletas\n            timestamps.removeAll { now.timeIntervalSince($0) > windowDuration }\n            \n            if timestamps.count < maxRequests {\n                timestamps.append(now)\n                return true\n            }\n            return false\n        }\n    }\n}\n\n// Ejemplo: no más de 5 solicitudes en 10 segundos\nlet windowLimiter = SlidingWindowLimiter(maxRequests: 5, windowDuration: 10)
\n\n

3. Throttling con DispatchQueue

\n

Utiliza los mecanismos integrados de GCD para forzar un retraso

Recommendations