Files
purchases-ios/Sources/Caching/KeyedDeferredValueStore.swift
T
Jacob Rakidzich f730d9df52 Create File Repository for use in upcoming feature work (#5477)
* Pull files from staging branch to introduce a file repository and intelligent task sharing

* removed typed throws and fix some docs

* Make tests satisfy predictive swift 6 failures

* @_spi(Internal) public

* Stop using the shared instance in an instance method

* Pull cache directory logic out of file repository

* update cache naming conventions

* @fire-at-will feedback -> use url instead of path

* Use strings file pattern

* remove priority from repository

* Remove RC Internal access from keyed deferred value store

* Remove spi internal from cache and network service

* makeSUT -> makeSystemUnderTest

* remove public scope in extensions

* rename function for clarity
2025-08-21 05:41:52 -05:00

75 lines
2.2 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
//
// KeyedDeferredValueStore.swift
//
// Created by Jacob Zivan Rakidzich on 8/12/25.
import Foundation
/// Some Task in which the value is Sendable and it can result in an error
typealias AnyTask<T: Sendable> = Task<T, Error>
/// Holds onto ``AnyTask`` objects by key, autoclearing on failure
actor KeyedDeferredValueStore<H: Hashable, T: Sendable> {
var deferred: [H: AnyTask<T>] = [:]
/// Sets the task in the cache if one is not found
/// - Parameters:
/// - task: The function that should execute if one is not found
/// - key: The key to look up the result by
/// - Returns: The stored task
func getOrPut(
_ task: @escaping @Sendable @autoclosure () -> AnyTask<T>,
forKey key: H
) -> AnyTask<T> {
guard let result = self.deferred[key] else {
let wrapped: AnyTask<T> = self.forgettingFailure(task, forKey: key)
self.deferred[key] = wrapped
return wrapped
}
return result
}
/// Replaces a task in the store
/// - Parameter task: The new function to store
/// - Parameter key: The key to look up the result by
/// - Returns: The stored task
func replaceValue(
_ task: @escaping @Sendable @autoclosure () -> AnyTask<T>,
forKey key: H
) -> AnyTask<T> {
let result = self.forgettingFailure(task, forKey: key)
self.deferred[key] = result
return result
}
/// Removes all cached tasks
func clear() {
self.deferred = [:]
}
private func forgettingFailure(
_ task: @escaping @Sendable () -> AnyTask<T>,
forKey key: H
) -> AnyTask<T> {
Task {
do {
return try await task().value
} catch {
self.deferred.removeValue(forKey: key)
throw error
}
}
}
/// Create a KeyedDeferredValueStore
init() {}
}