mirror of
https://github.com/Ranchero-Software/NetNewsWire
synced 2025-08-12 06:26:36 +00:00
Add Core module with Cache.
This commit is contained in:
21
Core/Package.swift
Normal file
21
Core/Package.swift
Normal 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"]
|
||||
),
|
||||
]
|
||||
)
|
||||
85
Core/Sources/Core/Cache.swift
Normal file
85
Core/Sources/Core/Cache.swift
Normal 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()
|
||||
}
|
||||
}
|
||||
12
Core/Tests/CoreTests/CoreTests.swift
Normal file
12
Core/Tests/CoreTests/CoreTests.swift
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user