Rate Limiting in Swift: Request Frequency Control for iOS and macOS

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Rate Limiting in Swift: A Complete Guide for iOS and macOS Applications



Rate limiting is a critical mechanism for the stable operation of mobile and desktop applications. In the Swift ecosystem (iOS/macOS), this approach allows you to control the number of API calls, I/O operations, or any repetitive actions per unit of time. Without rate limiting, your application risks being blocked by the server, wasting battery life on endless retries, or creating excessive network load.



Why is request frequency control needed in Swift?



  • Protection against exceeding API limits — many servers (e.g., GitHub, Twitter, Stripe) set strict quotas on the number of requests per minute/hour.
  • Performance optimization — preventing a "storm" of calls during fast scrolling or animations.
  • Saving traffic and battery — especially relevant for mobile devices.
  • Compliance with App Store policies — excessive background requests can lead to application rejection.


Basic approaches to implementing Rate Limiting in Swift



Let's look at three basic patterns that can be implemented without third-party libraries:



1. Token Bucket

A classic algorithm where "tokens" are added at a fixed rate. Each request consumes one token. If tokens run out, the request is rejected.



import Foundation

final class TokenBucket { private let capacity: Int private var tokens: Int private let refillRate: Double // tokens per second private var lastRefill: Date private let queue = DispatchQueue(label: "token.bucket.queue") init(capacity: Int, refillRate: Double) { self.capacity = capacity self.tokens = capacity self.refillRate = refillRate self.lastRefill = Date() } func tryConsume() -> Bool { queue.sync { refill() if tokens > 0 { tokens -= 1 return true } return false } } private func refill() { let now = Date() let elapsed = now.timeIntervalSince(lastRefill) let newTokens = Int(elapsed * refillRate) if newTokens > 0 { tokens = min(capacity, tokens + newTokens) lastRefill = now } }}

// Usage examplelet limiter = TokenBucket(capacity: 10, refillRate: 2.0) // 10 requests, 2 per secondif limiter.tryConsume() { print("✅ Request allowed")} else { print("⛔️ Limit exceeded, wait")}


2. Sliding Window Algorithm

Stores timestamps of the last N successful requests. If the window is full, it blocks new calls.



final class SlidingWindowLimiter {    private let maxRequests: Int    private let windowDuration: TimeInterval    private var timestamps: [Date] = []    private let queue = DispatchQueue(label: "sliding.window.queue")        init(maxRequests: Int, windowDuration: TimeInterval = 60) {        self.maxRequests = maxRequests        self.windowDuration = windowDuration    }        func canProceed() -> Bool {        queue.sync {            let now = Date()            // Remove outdated timestamps            timestamps.removeAll { now.timeIntervalSince($0) > windowDuration }                        if timestamps.count < maxRequests {                timestamps.append(now)                return true            }            return false        }    }}

// Example: no more than 5 requests per 10 secondslet windowLimiter = SlidingWindowLimiter(maxRequests: 5, windowDuration: 10)


3. Throttling with DispatchQueue

Uses built-in GCD mechanisms for forced de

Recommendations