Add Core module with Cache.

This commit is contained in:
Brent Simmons
2024-12-13 08:57:19 -08:00
parent 566fc0d1c2
commit 25c54a4c1d
4 changed files with 120 additions and 0 deletions

21
Core/Package.swift Normal file
View File

@@ -0,0 +1,21 @@
// swift-tools-version: 6.0
import PackageDescription
let package = Package(
name: "Core",
platforms: [.macOS(.v13), .iOS(.v16)],
products: [
.library(
name: "Core",
targets: ["Core"]),
],
targets: [
.target(
name: "Core"),
.testTarget(
name: "CoreTests",
dependencies: ["Core"]
),
]
)

View File

@@ -0,0 +1,85 @@
//
// Cache.swift
//
//
// Created by Brent Simmons on 10/12/24.
//
import Foundation
import os
public protocol CacheRecord: Sendable {
var dateCreated: Date { get }
}
public final class Cache<T: CacheRecord>: Sendable {
public let timeToLive: TimeInterval
public let timeBetweenCleanups: TimeInterval
private struct State: Sendable {
var lastCleanupDate = Date()
var cache = [String: T]()
}
private let stateLock = OSAllocatedUnfairLock(initialState: State())
public init(timeToLive: TimeInterval, timeBetweenCleanups: TimeInterval) {
self.timeToLive = timeToLive
self.timeBetweenCleanups = timeBetweenCleanups
}
public subscript(_ key: String) -> T? {
get {
stateLock.withLock { state in
cleanupIfNeeded(&state)
guard let value = state.cache[key] else {
return nil
}
if value.dateCreated.timeIntervalSinceNow < -timeToLive {
state.cache[key] = nil
return nil
}
return value
}
}
set {
stateLock.withLock { state in
state.cache[key] = newValue
}
}
}
public func cleanup() {
stateLock.withLock { state in
cleanupIfNeeded(&state)
}
}
}
extension Cache {
private func cleanupIfNeeded(_ state: inout State) {
let currentDate = Date()
guard state.lastCleanupDate.timeIntervalSince(currentDate) < -timeBetweenCleanups else {
return
}
var keysToDelete = [String]()
for (key, value) in state.cache {
if value.dateCreated.timeIntervalSince(currentDate) < -timeToLive {
keysToDelete.append(key)
}
}
for key in keysToDelete {
state.cache[key] = nil
}
state.lastCleanupDate = Date()
}
}

View File

@@ -0,0 +1,12 @@
import XCTest
@testable import Core
final class CoreTests: XCTestCase {
func testExample() throws {
// XCTest Documentation
// https://developer.apple.com/documentation/xctest
// Defining Test Cases and Test Methods
// https://developer.apple.com/documentation/xctest/defining_test_cases_and_test_methods
}
}

View File

@@ -1029,6 +1029,7 @@
8405DDA122168920008CE1BF /* TimelineTableView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TimelineTableView.xib; sourceTree = "<group>"; };
8405DDA422168C62008CE1BF /* TimelineContainerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineContainerViewController.swift; sourceTree = "<group>"; };
840BEE4021D70E64009BBAFA /* CrashReportWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CrashReportWindowController.swift; sourceTree = "<group>"; };
840C544F2D0C9A4A00A240DB /* Core */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Core; sourceTree = "<group>"; };
840D617C2029031C009BC708 /* NetNewsWire.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NetNewsWire.app; sourceTree = BUILT_PRODUCTS_DIR; };
840D617E2029031C009BC708 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
840D61952029031D009BC708 /* NetNewsWire_iOSTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetNewsWire_iOSTests.swift; sourceTree = "<group>"; };
@@ -2028,6 +2029,7 @@
51CD32C724D2E06C009ABAEF /* Secrets */,
51CD32A824D2CB25009ABAEF /* SyncDatabase */,
843E2F152CF2B43700ED170F /* RSWeb */,
840C544F2D0C9A4A00A240DB /* Core */,
);
sourceTree = "<group>";
usesTabs = 1;