Files
purchases-ios/Sources/Misc/RateLimiter.swift
T
13942b8818 [EXTERNAL] Lock RateLimiter.shouldProceed() entirely to avoid race conditions (#4635) via @nguyenhuy (#4637)
* [EXTERNAL] Lock RateLimiter.shouldProceed() entirely to avoid race conditions (#4635)

* Lock RateLimiter.shouldProceed() entirely to avoid race conditions

Android does this as well: https://github.com/RevenueCat/purchases-android/blob/eb3c8f4aa1a6993f12d6e2f93cc82d26063101a6/purchases/src/main/kotlin/com/revenuecat/purchases/utils/RateLimiter.kt#L10

* Fix typo in RateLimiterTests' file name

* Update Sources/Misc/RateLimiter.swift

As suggested by @facumenzella

Co-authored-by: Facundo Menzella <facumenzella@users.noreply.github.com>

---------

via @nguyenhuy

* Update Sources/Misc/RateLimiter.swift via @nguyenhuy

Co-authored-by: Huy Nguyen <hi@huynguyen.dev>

---------

Co-authored-by: Huy Nguyen <hi@huynguyen.dev>
2025-01-08 14:20:04 +01:00

50 lines
1.4 KiB
Swift

//
// Copyright RevenueCat Inc. All Rights Reserved.
//
// Licensed under the MIT License (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://opensource.org/licenses/MIT
//
// RateLimiter.swift
//
// Created by Josh Holtz on 2/27/24.
import Foundation
internal final class RateLimiter: @unchecked Sendable {
private let lock = Lock()
private var timestamps: [Date?]
private var index: Int = 0
private let maxCallsInclusive: Int
let maxCalls: Int
let period: TimeInterval
init(maxCalls: Int, period: TimeInterval) {
self.maxCalls = maxCalls
self.maxCallsInclusive = self.maxCalls + 1
self.period = period
self.timestamps = Array(repeating: nil, count: maxCallsInclusive)
}
func shouldProceed() -> Bool {
return self.lock.perform {
let now = Date()
let oldestIndex = (index + 1) % maxCallsInclusive
let oldestTimestamp = timestamps[oldestIndex]
// Check if the oldest timestamp is outside the rate limiting period or if it's nil
if let oldestTimestamp = oldestTimestamp, now.timeIntervalSince(oldestTimestamp) <= period {
return false
} else {
timestamps[index] = now
index = oldestIndex
return true
}
}
}
}