From fab0df9723265d4f7839a54f1f2ee50d842cabd4 Mon Sep 17 00:00:00 2001 From: Ibrahim Koteish <2681868+ibrahimkteish@users.noreply.github.com> Date: Fri, 31 Jul 2026 09:16:16 +0200 Subject: [PATCH 1/4] Use default key base for reader loading --- Sources/Sharing/SharedReaderKey.swift | 8 +++++ Tests/SharingTests/DefaultTests.swift | 51 +++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/Sources/Sharing/SharedReaderKey.swift b/Sources/Sharing/SharedReaderKey.swift index 6e8aba27..357515ec 100644 --- a/Sources/Sharing/SharedReaderKey.swift +++ b/Sources/Sharing/SharedReaderKey.swift @@ -153,6 +153,10 @@ extension SharedReader { /// /// - Parameter key: A shared key associated with the shared reference. It is responsible for /// loading the shared reference's value from some external source. + public func load>(_ key: K.Default) async throws { + try await load(key.base) + } + public func load(_ key: some SharedReaderKey) async throws { @Dependency(PersistentReferences.self) var persistentReferences SharedPublisherLocals.$isLoading.withValue(true) { @@ -184,6 +188,10 @@ extension SharedReader { /// /// - Parameter key: A shared key associated with the shared reference. It is responsible for /// loading the shared reference's value from some external source. + public init>(require key: K.Default) async throws { + try await self.init(require: key.base) + } + public init(require key: some SharedReaderKey) async throws { let value = try await withUnsafeThrowingContinuation { continuation in key.load( diff --git a/Tests/SharingTests/DefaultTests.swift b/Tests/SharingTests/DefaultTests.swift index bf2a707b..637465c9 100644 --- a/Tests/SharingTests/DefaultTests.swift +++ b/Tests/SharingTests/DefaultTests.swift @@ -1,3 +1,4 @@ +import ConcurrencyExtras import Sharing import Testing @@ -149,6 +150,56 @@ import Testing #expect(count == 3) #expect(countReader == 3) } + + @Test func loadDefaultKeyUsesBaseReference() async throws { + let subscriptionCount = LockIsolated(0) + let key = SubscriptionCountingKey( + id: "loadDefaultKeyUsesBaseReference", + subscriptionCount: subscriptionCount + ) + let defaultKey = SubscriptionCountingKey.Default[key, default: 0] + + @SharedReader(value: 0) var value + try await $value.load(defaultKey) + @SharedReader(defaultKey) var otherValue + + withExtendedLifetime(($value, $otherValue)) { + #expect(subscriptionCount.value == 1) + } + } + + @Test func requireDefaultKeyUsesBaseReference() async throws { + let subscriptionCount = LockIsolated(0) + let key = SubscriptionCountingKey( + id: "requireDefaultKeyUsesBaseReference", + subscriptionCount: subscriptionCount + ) + let defaultKey = SubscriptionCountingKey.Default[key, default: 0] + + let value = try await SharedReader(require: defaultKey) + @SharedReader(defaultKey) var otherValue + + withExtendedLifetime((value, $otherValue)) { + #expect(subscriptionCount.value == 1) + } + } + } +} + +private struct SubscriptionCountingKey: SharedReaderKey { + let id: String + let subscriptionCount: LockIsolated + + func load(context: LoadContext, continuation: LoadContinuation) { + continuation.resume(returning: 0) + } + + func subscribe( + context: LoadContext, + subscriber: SharedSubscriber + ) -> SharedSubscription { + subscriptionCount.withValue { $0 += 1 } + return SharedSubscription {} } } From 519bb9bc0518dab14ad6010746c5365dcc821fa4 Mon Sep 17 00:00:00 2001 From: Ibrahim Koteish <2681868+ibrahimkteish@users.noreply.github.com> Date: Fri, 31 Jul 2026 09:16:16 +0200 Subject: [PATCH 2/4] Remove explicit lifetime extension from tests --- Tests/SharingTests/DefaultTests.swift | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Tests/SharingTests/DefaultTests.swift b/Tests/SharingTests/DefaultTests.swift index 637465c9..684c5302 100644 --- a/Tests/SharingTests/DefaultTests.swift +++ b/Tests/SharingTests/DefaultTests.swift @@ -163,9 +163,7 @@ import Testing try await $value.load(defaultKey) @SharedReader(defaultKey) var otherValue - withExtendedLifetime(($value, $otherValue)) { - #expect(subscriptionCount.value == 1) - } + #expect(subscriptionCount.value == 1) } @Test func requireDefaultKeyUsesBaseReference() async throws { @@ -179,9 +177,7 @@ import Testing let value = try await SharedReader(require: defaultKey) @SharedReader(defaultKey) var otherValue - withExtendedLifetime((value, $otherValue)) { - #expect(subscriptionCount.value == 1) - } + #expect(subscriptionCount.value == 1) } } } From 0acfee715198a5b633edb801e2d124af5295e87b Mon Sep 17 00:00:00 2001 From: Ibrahim Koteish <2681868+ibrahimkteish@users.noreply.github.com> Date: Fri, 31 Jul 2026 09:16:16 +0200 Subject: [PATCH 3/4] Document default-key reader overloads --- Sources/Sharing/SharedReaderKey.swift | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/Sources/Sharing/SharedReaderKey.swift b/Sources/Sharing/SharedReaderKey.swift index 357515ec..6b298b11 100644 --- a/Sources/Sharing/SharedReaderKey.swift +++ b/Sources/Sharing/SharedReaderKey.swift @@ -149,14 +149,16 @@ extension SharedReader { self.init(wrappedValue: wrappedValue(), key.base) } - /// Replaces a shared reference's key and attempts to load its value. - /// - /// - Parameter key: A shared key associated with the shared reference. It is responsible for - /// loading the shared reference's value from some external source. + /// Replaces a shared reference's key with a shared key that provides a default value and + /// attempts to load its value. public func load>(_ key: K.Default) async throws { try await load(key.base) } + /// Replaces a shared reference's key and attempts to load its value. + /// + /// - Parameter key: A shared key associated with the shared reference. It is responsible for + /// loading the shared reference's value from some external source. public func load(_ key: some SharedReaderKey) async throws { @Dependency(PersistentReferences.self) var persistentReferences SharedPublisherLocals.$isLoading.withValue(true) { @@ -177,6 +179,12 @@ extension SharedReader { try await load(key) } + /// Creates a shared reference from a shared key that provides a default value by loading it from + /// its external source. + public init>(require key: K.Default) async throws { + try await self.init(require: key.base) + } + /// Creates a shared reference to a read-only value using a shared key by loading it from its /// external source. /// @@ -188,10 +196,6 @@ extension SharedReader { /// /// - Parameter key: A shared key associated with the shared reference. It is responsible for /// loading the shared reference's value from some external source. - public init>(require key: K.Default) async throws { - try await self.init(require: key.base) - } - public init(require key: some SharedReaderKey) async throws { let value = try await withUnsafeThrowingContinuation { continuation in key.load( From a0580494722051500f4c13a8fa0be1eb47a16c97 Mon Sep 17 00:00:00 2001 From: Ibrahim Koteish <2681868+ibrahimkteish@users.noreply.github.com> Date: Fri, 31 Jul 2026 09:16:16 +0200 Subject: [PATCH 4/4] Use default key base when loading shared values --- Sources/Sharing/SharedKey.swift | 12 +++++++++ Tests/SharingTests/DefaultTests.swift | 35 ++++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/Sources/Sharing/SharedKey.swift b/Sources/Sharing/SharedKey.swift index e64899cd..3581d9a1 100644 --- a/Sources/Sharing/SharedKey.swift +++ b/Sources/Sharing/SharedKey.swift @@ -82,6 +82,12 @@ extension Shared { self.init(wrappedValue: wrappedValue(), key.base) } + /// Replaces a shared reference's key with a shared key that provides a default value and + /// attempts to load its value. + public func load>(_ key: K.Default) async throws { + try await load(key.base) + } + /// Replaces a shared reference's key and attempts to load its value. /// /// - Parameter key: A shared key associated with the shared reference. It is responsible for @@ -100,6 +106,12 @@ extension Shared { try await load() } + /// Creates a shared reference from a shared key that provides a default value by loading it + /// from its external source. + public init>(require key: K.Default) async throws { + try await self.init(require: key.base) + } + /// Creates a shared reference to a value using a shared key by loading it from its external /// source. /// diff --git a/Tests/SharingTests/DefaultTests.swift b/Tests/SharingTests/DefaultTests.swift index 684c5302..990f2163 100644 --- a/Tests/SharingTests/DefaultTests.swift +++ b/Tests/SharingTests/DefaultTests.swift @@ -179,10 +179,39 @@ import Testing #expect(subscriptionCount.value == 1) } + + @Test func sharedLoadDefaultKeyUsesBaseReference() async throws { + let subscriptionCount = LockIsolated(0) + let key = SubscriptionCountingKey( + id: "sharedLoadDefaultKeyUsesBaseReference", + subscriptionCount: subscriptionCount + ) + let defaultKey = SubscriptionCountingKey.Default[key, default: 0] + + @Shared(value: 0) var value + try await $value.load(defaultKey) + @Shared(defaultKey) var otherValue + + #expect(subscriptionCount.value == 1) + } + + @Test func sharedRequireDefaultKeyUsesBaseReference() async throws { + let subscriptionCount = LockIsolated(0) + let key = SubscriptionCountingKey( + id: "sharedRequireDefaultKeyUsesBaseReference", + subscriptionCount: subscriptionCount + ) + let defaultKey = SubscriptionCountingKey.Default[key, default: 0] + + let value = try await Shared(require: defaultKey) + @Shared(defaultKey) var otherValue + + #expect(subscriptionCount.value == 1) + } } } -private struct SubscriptionCountingKey: SharedReaderKey { +private struct SubscriptionCountingKey: SharedKey { let id: String let subscriptionCount: LockIsolated @@ -197,6 +226,10 @@ private struct SubscriptionCountingKey: SharedReaderKey { subscriptionCount.withValue { $0 += 1 } return SharedSubscription {} } + + func save(_ value: Int, context: SaveContext, continuation: SaveContinuation) { + continuation.resume() + } } extension SharedReaderKey where Self == InMemoryKey.Default {