Rate Limiting в Swift: управление частотой запросов в iOS/macOS

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Rate Limiting в Swift (iOS/macOS): Полное руководство

Rate Limiting (ограничение частоты запросов) — критически важный механизм для стабильной работы мобильных и десктопных приложений. В экосистеме Swift (iOS/macOS) правильное управление частотой вызовов API, отправки данных или выполнения фоновых задач предотвращает перегрузку серверов, экономит трафик и защищает от злоупотреблений. В этой статье мы подробно разберём библиотеки и инструменты для реализации Rate Limiting на Swift, включая алгоритмы, примеры кода и лучшие практики.

Почему Rate Limiting важен для iOS/macOS?

Приложения для Apple-устройств часто взаимодействуют с удалёнными серверами: загружают контент, синхронизируют данные, отправляют аналитику. Без ограничений:

  • Сервер может заблокировать приложение за превышение лимита (HTTP 429 Too Many Requests).
  • Аккумулятор устройства быстро разряжается из-за частых сетевых вызовов.
  • Пользовательский интерфейс становится неотзывчивым при лавинообразных запросах.

Основные алгоритмы Rate Limiting

В Swift чаще всего применяются три классических алгоритма:

  • Token Bucket (Корзина токенов) — токены накапливаются с постоянной скоростью, каждый запрос потребляет один токен. Подходит для равномерной нагрузки.
  • Leaky Bucket (Дырявое ведро) — запросы обрабатываются с фиксированной скоростью, избыточные отбрасываются или ставятся в очередь.
  • Sliding Window (Скользящее окно) — подсчёт запросов за последние N секунд/минут. Гибкий, но требует больше памяти.

Популярные библиотеки для Rate Limiting в Swift

1. Alamofire (встроенные механизмы)

Хотя Alamofire — это HTTP-клиент, он предоставляет инструменты для ограничения запросов через RequestInterceptor. Вы можете создать собственный интерсептор, который будет проверять количество запросов и добавлять задержки.

import Alamofire

class RateLimiter: RequestInterceptor {
    private let queue = DispatchQueue(label: "rate.limiter")
    private var lastRequestTime: Date?
    private let minimumInterval: TimeInterval = 1.0 // 1 секунда между запросами
    
    func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) {
        queue.sync {
            if let last = lastRequestTime, Date().timeIntervalSince(last) < minimumInterval {
                let delay = minimumInterval - Date().timeIntervalSince(last)
                Thread.sleep(forTimeInterval: delay)
            }
            lastRequestTime = Date()
        }
        completion(.success(urlRequest))
    }
}

let session = Session(interceptor: RateLimiter())

2. SwiftyBeaver (логирование с ограничением)

Для ограничения частоты логов используйте SBPlatformDestination с настройкой minLevel и пользовательским фильтром. Но для полноценного Rate Limiting лучше комбинировать с отдельной библиотекой.

3. RateLimit (специализированная библиотека)

Открытая библиотека RateLimit (доступна через Swift Package Manager) реализует алгоритм Token Bucket. Она легковесна и идеально подходит для iOS/macOS.

import RateLimit

let limiter = TokenBucket(capacity: 10, fillRate: 1) // 10 запросов в секунду

func makeRequest() {
    guard limiter.consume() else {
        print("Rate limit exceeded. Request blocked.")
        return
    }
    // Выполнение сетевого запроса
    print("Request sent.")
}

4. NetworkThrottle (продвинутый инструмент)

Библиотека NetworkThrottle специализируется на эмуляции медленных сетей и ограничении скорости запросов. Полезна для тестирования и отладки.

import NetworkThrottle

let throttle = NetworkThrottle(limit: 5, per: 60) // 5 запросов в минуту
throttle.execute {
    // Ваш запрос
    print("Request allowed")
}

Реализация собственного Rate Limiter без сторонних библиотек

Если вы не хотите зависеть от внешних зависимостей, напишите простой Rate Limiter на основе DispatchQueue и Date:

class SimpleRateLimiter {
    private var timestamps: [Date] = []
    private let maxRequests: Int
    private let window: TimeInterval
    private let queue = DispatchQueue(label: "rate.limiter.queue")
    
    init(maxRequests: Int, perWindow window: TimeInterval) {
        self.maxRequests = maxRequests
        self.window = window
    }
    
    func allow() -> Bool {
        queue.sync {
            let now = Date()
            timestamps = timestamps.filter { now.timeInterval                    

Рекомендаций