diff --git a/Sources/Dependencies/DependencyValues.swift b/Sources/Dependencies/DependencyValues.swift index eda32d55..a061e381 100644 --- a/Sources/Dependencies/DependencyValues.swift +++ b/Sources/Dependencies/DependencyValues.swift @@ -388,6 +388,24 @@ public struct DependencyValues: Sendable { public func resetCache() { cachedValues.cached = [:] } + + // Allows you to reset a single dependency key + public static func reset(_ keyPath: KeyPath & Sendable) { + reset(by: TypeIdentifier(Value.self)) + } + + // Allows you to reset a single dependency key + public static func reset(_ key: Key.Type) { + reset(by: TypeIdentifier(Key.self)) + } + + private static func reset(by typeId: TypeIdentifier) { + let context = + _current.storage[ObjectIdentifier(DependencyContextKey.self)] as? DependencyContext + ?? defaultContext + let cachedKey = CachedValues.CacheKey(id: typeId, context: context) + _current.cachedValues.cached.removeValue(forKey: cachedKey) + } } struct CurrentDependency { diff --git a/Tests/DependenciesTests/DependencyValuesTests.swift b/Tests/DependenciesTests/DependencyValuesTests.swift index 7f3fcd91..b7681ec9 100644 --- a/Tests/DependenciesTests/DependencyValuesTests.swift +++ b/Tests/DependenciesTests/DependencyValuesTests.swift @@ -293,6 +293,44 @@ final class DependencyValuesTests: XCTestCase { XCTAssertEqual(reuseClient.count(), 0) #endif } + + #if !os(Linux) && !os(WASI) && !os(Windows) + func testFullDependencyReset_KeyPathLiveContext() async { + await withDependencies { + $0.context = .live + } operation: { + @Dependency(\.cachedDependency) var cachedDependency: CachedDependency + var value = await cachedDependency.increment() + XCTAssertEqual(value, 1) + value = await cachedDependency.increment() + XCTAssertEqual(value, 2) + value = await cachedDependency.increment() + XCTAssertEqual(value, 3) + DependencyValues.reset(\.cachedDependency) + value = await cachedDependency.increment() + XCTAssertEqual(value, 1) + } + } + #endif + + #if !os(Linux) && !os(WASI) && !os(Windows) + func testFullDependencyReset_ObjectKeyLiveContext() async { + await withDependencies { + $0.context = .live + } operation: { + @Dependency(\.cachedDependency) var cachedDependency: CachedDependency + var value = await cachedDependency.increment() + XCTAssertEqual(value, 1) + value = await cachedDependency.increment() + XCTAssertEqual(value, 2) + value = await cachedDependency.increment() + XCTAssertEqual(value, 3) + DependencyValues.reset(CachedDependency.self) + value = await cachedDependency.increment() + XCTAssertEqual(value, 1) + } + } + #endif func testBinding() { withDependencies { @@ -927,7 +965,11 @@ extension DependencyValues { } } -actor CachedDependency: TestDependencyKey { +actor CachedDependency: DependencyKey { + static var liveValue: CachedDependency { + CachedDependency() + } + static var testValue: CachedDependency { CachedDependency() } diff --git a/Tests/DependenciesTests/SwiftTestingTests.swift b/Tests/DependenciesTests/SwiftTestingTests.swift index ef45b7c3..f7193deb 100644 --- a/Tests/DependenciesTests/SwiftTestingTests.swift +++ b/Tests/DependenciesTests/SwiftTestingTests.swift @@ -49,6 +49,38 @@ import ConcurrencyExtras #expect(value == 1) #endif } + + @Test + func cacheTargetedKeypathReset_liveContext() { + withDependencies { + $0.context = .live + } operation: { + @Dependency(Client.self) var client + var value = client.increment() + #expect(value == 1) + value = client.increment() + #expect(value == 2) + DependencyValues.reset(\.client) + value = client.increment() + #expect(value == 1) + } + } + + @Test + func cacheTargetedTypeReset_liveContext() { + withDependencies { + $0.context = .live + } operation: { + @Dependency(Client.self) var client + var value = client.increment() + #expect(value == 1) + value = client.increment() + #expect(value == 2) + DependencyValues.reset(Client.self) + value = client.increment() + #expect(value == 1) + } + } @Test(.dependency(\.date.now, Date(timeIntervalSinceReferenceDate: 0))) func trait() { @@ -94,9 +126,17 @@ import ConcurrencyExtras } } - private struct Client: TestDependencyKey { + private struct Client: DependencyKey { var increment: @Sendable () -> Int + static var liveValue: Client { + getClient() + } + static var testValue: Client { + getClient() + } + + static func getClient() -> Client { let count = LockIsolated(0) return Self { count.withValue { @@ -106,6 +146,12 @@ import ConcurrencyExtras } } } + private extension DependencyValues { + var client: Client { + get { self[Client.self] } + set { self[Client.self] = newValue } + } + } class ClassClient { var count = 0