Last active
January 18, 2025 22:51
-
-
Save Aayush9029/40afffc39c358c346f248b37704764b6 to your computer and use it in GitHub Desktop.
Local Interval Client
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Dependencies | |
import Foundation | |
// MARK: - Key Definition | |
public struct IntervalKey { | |
let key: String | |
let config: IntervalConfig | |
public init(_ key: String, config: IntervalConfig) { | |
self.key = "interval." + key | |
self.config = config | |
} | |
} | |
// MARK: - Interval Configuration | |
public struct IntervalConfig: Equatable { | |
let component: Calendar.Component | |
let value: Int | |
public static let hourly = Self(component: .hour, value: 1) | |
public static let daily = Self(component: .day, value: 1) | |
public static let weekly = Self(component: .weekOfYear, value: 1) | |
public static let monthly = Self(component: .month, value: 1) | |
public static let yearly = Self(component: .year, value: 1) | |
public static func custom(_ component: Calendar.Component, _ value: Int) -> Self { | |
Self(component: component, value: value) | |
} | |
} | |
// MARK: - Keys Extension Support | |
public extension IntervalKey { | |
static func key(_ name: String, _ config: IntervalConfig) -> IntervalKey { | |
IntervalKey(name, config: config) | |
} | |
} | |
// MARK: - Client Interface | |
public struct IntervalClient { | |
public var shouldRun: @Sendable (IntervalKey) -> Bool | |
public var markRun: @Sendable (IntervalKey) async -> Void | |
public var reset: @Sendable (IntervalKey) async -> Void | |
public var resetAll: @Sendable () async -> Void | |
} | |
// MARK: - Live Implementation | |
extension IntervalClient: DependencyKey { | |
public static let liveValue = Self( | |
shouldRun: { key in | |
let defaults = UserDefaults.standard | |
let lastRun = defaults.integer(forKey: key.key) | |
let currentValue = Calendar.current.component( | |
key.config.component, | |
from: Date() | |
) | |
return currentValue > lastRun + key.config.value - 1 | |
}, | |
markRun: { key in | |
let currentValue = Calendar.current.component( | |
key.config.component, | |
from: Date() | |
) | |
UserDefaults.standard.set(currentValue, forKey: key.key) | |
}, | |
reset: { key in | |
UserDefaults.standard.removeObject(forKey: key.key) | |
}, | |
resetAll: { | |
let defaults = UserDefaults.standard | |
let intervalKeys = defaults.dictionaryRepresentation().keys.filter { | |
$0.hasPrefix("interval.") | |
} | |
intervalKeys.forEach { defaults.removeObject(forKey: $0) } | |
} | |
) | |
} | |
// MARK: - Test Support | |
extension IntervalClient: TestDependencyKey { | |
public static let testValue = Self( | |
shouldRun: unimplemented("IntervalClient.shouldRun"), | |
markRun: unimplemented("IntervalClient.markRun"), | |
reset: unimplemented("IntervalClient.reset"), | |
resetAll: unimplemented("IntervalClient.resetAll") | |
) | |
} | |
public extension IntervalClient { | |
static let noop = Self( | |
shouldRun: { _ in true }, | |
markRun: { _ in }, | |
reset: { _ in }, | |
resetAll: {} | |
) | |
} | |
public extension DependencyValues { | |
var intervalClient: IntervalClient { | |
get { self[IntervalClient.self] } | |
set { self[IntervalClient.self] = newValue } | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
IntervalClient
A dependency for managing time-based intervals and tracking their completion. Perfect for features that should reset and be available again after a certain time period (daily, weekly, monthly, etc.).
How It Works
IntervalClient manages two states:
An interval
shouldRun
returnstrue
only when BOTH conditions are met:The interval automatically resets after its configured time period (e.g., daily, weekly, monthly).
Usage
1. Define Your Intervals
2. Use in Your Features
Available Intervals
Testing
It's not completely tested, but makes sense logically to me. (which is not the best way to do it)