Rate Limiting в Swift: Полное руководство для iOS и macOS приложений
Rate limiting (ограничение частоты запросов) — критически важный механизм для стабильной работы мобильных и десктопных приложений. В экосистеме Swift (iOS/macOS) этот подход позволяет контролировать количество вызовов API, операций ввода-вывода или любых повторяющихся действий в единицу времени. Без rate limiting ваше приложение рискует быть заблокированным сервером, потратить заряд батареи на бесконечные ретраи или создать чрезмерную нагрузку на сеть.
Зачем нужно ограничение частоты запросов в Swift?
- Защита от превышения лимитов API — многие серверы (например, GitHub, Twitter, Stripe) устанавливают жесткие квоты на количество запросов в минуту/час.
- Оптимизация производительности — предотвращение "шторма" вызовов при быстром скролле или анимациях.
- Экономия трафика и батареи — особенно актуально для мобильных устройств.
- Соблюдение политик App Store — чрезмерные фоновые запросы могут привести к отклонению приложения.
Основные подходы к реализации Rate Limiting в Swift
Рассмотрим три базовых паттерна, которые можно реализовать без сторонних библиотек:
1. Токен-бакет (Token Bucket)
Классический алгоритм, где "токены" добавляются с фиксированной скоростью. Каждый запрос потребляет один токен. Если токены закончились — запрос отклоняется.
import Foundation
final class TokenBucket {
private let capacity: Int
private var tokens: Int
private let refillRate: Double // токенов в секунду
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
}
}
}
// Пример использования
let limiter = TokenBucket(capacity: 10, refillRate: 2.0) // 10 запросов, 2 в секунду
if limiter.tryConsume() {
print("✅ Запрос разрешен")
} else {
print("⛔️ Превышен лимит, ждите")
}
2. Оконный алгоритм (Sliding Window)
Запоминает временные метки последних N успешных запросов. Если окно переполнено — блокирует новые вызовы.
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()
// Удаляем устаревшие метки
timestamps.removeAll { now.timeIntervalSince($0) > windowDuration }
if timestamps.count < maxRequests {
timestamps.append(now)
return true
}
return false
}
}
}
// Пример: не более 5 запросов за 10 секунд
let windowLimiter = SlidingWindowLimiter(maxRequests: 5, windowDuration: 10)
3. Throttling с DispatchQueue
Использует встроенные механизмы GCD для принудительной задержки между вызовами.
final class Throttler {
private var workItem: DispatchWorkItem?
private var lastFire: Date = .distantPast
private let queue: DispatchQueue
private let minimumDelay: TimeInterval
init(minimumDelay: TimeInterval, queue: DispatchQueue = .main) {
self.minimumDelay = minimumDelay
self.queue = queue
}
func throttle(_ block: @escaping () -> Void) {
workItem?.cancel()
let newItem = DispatchWorkItem { [weak self] in
self?.lastFire = Date()
block()
}
workItem = newItem
let delay = max(minimumDelay - Date().timeIntervalSince(lastFire), 0)
queue.asyncAfter(deadline: .now() + delay, execute: newItem)
}
}
// Использование для поискового поля
let searchThrottler = Throttler(minimumDelay: 0.3) // 300ms задержка
searchThrottler.throttle {
print("Выполняем поисковой запрос")
}
Популярные библиотеки для Rate Limiting в Swift
Также в библиотеке
Работа с изображениями в Swift: полное руководство для iOS и macOS
WebSocket библиотеки Kotlin для создания реального времени в веб-приложениях
Парсинг HTML в C++: обзор библиотек и примеры кода
Swagger/OpenAPI для C# (.NET): Генерация и документация API
Отправка SMS через API на Node.js: библиотека twilio