This commit is contained in:
Daniel Arantes Loverde
2026-07-07 15:11:31 -03:00
parent 8b9ebdcb44
commit e51c99973f
293 changed files with 457 additions and 4919 deletions

View File

@@ -0,0 +1,154 @@
import Foundation
#if canImport(UIKit)
import UIKit
typealias PlatformImage = UIImage
#elseif canImport(AppKit)
import AppKit
typealias PlatformImage = NSImage
#endif
enum AppCacheTTL {
static let twoHours: TimeInterval = 2 * 60 * 60
static let homeStores: TimeInterval = 5 * 60
}
enum AppCacheKey {
static let homeStoresLatestSnapshot = "home-stores.latest.snapshot"
}
final class AppContentCache: @unchecked Sendable {
static let shared = AppContentCache()
private struct Entry {
let value: Any
let expiry: Date
}
private var entries: [String: Entry] = [:]
private let queue = DispatchQueue(label: "com.pedifoods.content-cache", qos: .userInitiated)
private init() {}
func value<T>(for key: String, as type: T.Type = T.self) -> T? {
queue.sync {
guard let entry = entries[key] else { return nil }
if entry.expiry <= Date() {
entries.removeValue(forKey: key)
return nil
}
return entry.value as? T
}
}
func set<T>(_ value: T, for key: String, ttl: TimeInterval) {
queue.sync {
entries[key] = Entry(value: value, expiry: Date().addingTimeInterval(ttl))
}
}
func invalidate(prefix: String? = nil) {
queue.sync {
guard let prefix, prefix.isEmpty == false else {
entries.removeAll()
return
}
let keys = entries.keys.filter { $0.hasPrefix(prefix) }
for key in keys {
entries.removeValue(forKey: key)
}
}
}
}
#if canImport(UIKit) || canImport(AppKit)
final class AppImageCache: @unchecked Sendable {
static let shared = AppImageCache()
private struct Entry {
let image: PlatformImage
let expiry: Date
}
private var entries: [String: Entry] = [:]
private let queue = DispatchQueue(label: "com.pedifoods.image-cache", qos: .userInitiated)
private init() {
configureURLCacheIfNeeded()
}
func image(for url: URL, ttl: TimeInterval, forceRefresh: Bool = false) async -> PlatformImage? {
let key = url.absoluteString
let now = Date()
if forceRefresh == false {
let cached = queue.sync { entries[key] }
if let cached, cached.expiry > now {
return cached.image
}
}
var request = URLRequest(url: url)
request.timeoutInterval = 20
request.cachePolicy = forceRefresh ? .reloadIgnoringLocalCacheData : .returnCacheDataElseLoad
if forceRefresh == false,
let diskCached = URLCache.shared.cachedResponse(for: request),
let image = platformImage(from: diskCached.data) {
queue.sync {
entries[key] = Entry(image: image, expiry: now.addingTimeInterval(ttl))
}
return image
}
do {
let (data, response) = try await URLSession.shared.data(for: request)
guard let image = platformImage(from: data) else { return nil }
URLCache.shared.storeCachedResponse(CachedURLResponse(response: response, data: data), for: request)
queue.sync {
entries[key] = Entry(image: image, expiry: now.addingTimeInterval(ttl))
}
return image
} catch {
return nil
}
}
func invalidateAll() {
queue.sync {
entries.removeAll()
}
URLCache.shared.removeAllCachedResponses()
}
private func configureURLCacheIfNeeded() {
let current = URLCache.shared
let minMemoryCapacity = 64 * 1024 * 1024
let minDiskCapacity = 256 * 1024 * 1024
if current.memoryCapacity < minMemoryCapacity || current.diskCapacity < minDiskCapacity {
URLCache.shared = URLCache(memoryCapacity: minMemoryCapacity, diskCapacity: minDiskCapacity)
}
}
private func platformImage(from data: Data) -> PlatformImage? {
#if canImport(UIKit)
return UIImage(data: data)
#elseif canImport(AppKit)
return NSImage(data: data)
#else
return nil
#endif
}
}
#endif
#if !(canImport(UIKit) || canImport(AppKit))
final class AppImageCache: @unchecked Sendable {
static let shared = AppImageCache()
private init() {}
func invalidateAll() {}
}
#endif