Rate Limiting in Swift: Vollständige Anleitung für iOS- und macOS-Apps
\n\nRate Limiting (Begrenzung der Anforderungsrate) ist ein kritischer Mechanismus für den stabilen Betrieb von mobilen und Desktop-Anwendungen. Im Swift-Ökosystem (iOS/macOS) ermöglicht dieser Ansatz die Kontrolle der Anzahl von API-Aufrufen, E/A-Operationen oder beliebiger sich wiederholender Aktionen pro Zeiteinheit. Ohne Rate Limiting riskiert Ihre App, vom Server blockiert zu werden, Akkuladung durch endlose Wiederholungsversuche zu verschwenden oder übermäßige Netzlast zu erzeugen.
\n\nWarum ist die Begrenzung der Anforderungsrate in Swift notwendig?
\n\n- \n
- Schutz vor Überschreitung von API-Limits — viele Server (z. B. GitHub, Twitter, Stripe) legen strenge Kontingente für die Anzahl der Anfragen pro Minute/Stunde fest. \n
- Leistungsoptimierung — Verhinderung eines "Aufruf-Sturms" bei schnellem Scrollen oder Animationen. \n
- Einsparung von Datenvolumen und Akku — besonders relevant für mobile Geräte. \n
- Einhaltung der App Store-Richtlinien — übermäßige Hintergrundanfragen können zur Ablehnung der App führen. \n
Grundlegende Ansätze zur Implementierung von Rate Limiting in Swift
\n\nBetrachten wir drei grundlegende Muster, die ohne externe Bibliotheken implementiert werden können:
\n\n1. Token-Bucket (Token Bucket)
\nKlassischer Algorithmus, bei dem "Token" mit einer festen Rate hinzugefügt werden. Jede Anfrage verbraucht einen Token. Sind keine Token mehr vorhanden, wird die Anfrage abgelehnt.
\n\nimport Foundation\n\nfinal class TokenBucket {\n private let capacity: Int\n private var tokens: Int\n private let refillRate: Double // Token pro Sekunde\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// Beispiel zur Verwendung\nlet limiter = TokenBucket(capacity: 10, refillRate: 2.0) // 10 Anfragen, 2 pro Sekunde\nif limiter.tryConsume() {\n print("✅ Anfrage erlaubt")\n} else {\n print("⛔️ Limit überschritten, warten")\n}\n\n2. Sliding Window (Gleitendes Fenster)
\nSpeichert die Zeitstempel der letzten N erfolgreichen Anfragen. Wenn das Fenster voll ist, werden neue Aufrufe blockiert.
\n\nfinal 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 // Entferne veraltete Zeitstempel\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// Beispiel: maximal 5 Anfragen in 10 Sekunden\nlet windowLimiter = SlidingWindowLimiter(maxRequests: 5, windowDuration: 10)\n\n3. Throttling mit DispatchQueue
\nVerwendet integrierte GCD-Mechanismen zur erzwungenen Verzögerung
Auch in der Bibliothek
Caching in C++: Redis, Memcached und integrierte Lösungen
Fiber für Go: Vollständiges Tutorial mit Beispielen
Aufgabenwarteschlangen in Go: Überblick über die Bibliotheken async, gocelery und machinery