Skip to content

Commit 75b76a3

Browse files
committed
map
1 parent 8141fd7 commit 75b76a3

File tree

5 files changed

+103
-0
lines changed

5 files changed

+103
-0
lines changed

Sources/Sharing/Internal/Reference.swift

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,56 @@ where Base: MutableReference, Path: WritableKeyPath<Base.Value, Value> {
613613
}
614614
}
615615

616+
final class _ReadClosureReference<Base: Reference, Value>:
617+
Reference,
618+
Observable
619+
{
620+
private let base: Base
621+
private let body: @Sendable (Base.Value) -> Value
622+
623+
init(base: Base, body: @escaping @Sendable (Base.Value) -> Value) {
624+
self.base = base
625+
self.body = body
626+
}
627+
628+
var id: ObjectIdentifier {
629+
base.id
630+
}
631+
632+
var isLoading: Bool {
633+
base.isLoading
634+
}
635+
636+
var loadError: (any Error)? {
637+
base.loadError
638+
}
639+
640+
var wrappedValue: Value {
641+
body(base.wrappedValue)
642+
}
643+
644+
func load() async throws {
645+
try await base.load()
646+
}
647+
648+
func touch() {
649+
base.touch()
650+
}
651+
652+
#if canImport(Combine)
653+
var publisher: any Publisher<Value, Never> {
654+
func open(_ publisher: some Publisher<Base.Value, Never>) -> any Publisher<Value, Never> {
655+
publisher.map(body)
656+
}
657+
return open(base.publisher)
658+
}
659+
#endif
660+
661+
var description: String {
662+
".map(\(base.description), as: \(Value.self).self)"
663+
}
664+
}
665+
616666
final class _OptionalReference<Base: Reference<Value?>, Value>:
617667
Reference,
618668
Observable,

Sources/Sharing/Shared.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,15 @@ public struct Shared<Value> {
223223
reference = newValue.reference
224224
}
225225
}
226+
227+
/// Returns a read-only shared reference to the resulting value of a given closure.
228+
///
229+
/// - Returns: A new read-only shared reference.
230+
public func map<Member>(
231+
_ body: @escaping @Sendable(Value) -> Member
232+
) -> SharedReader<Member> {
233+
SharedReader(self).map(body)
234+
}
226235

227236
/// Returns a shared reference to the resulting value of a given key path.
228237
///

Sources/Sharing/SharedReader.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,20 @@ public struct SharedReader<Value> {
155155
reference = newValue.reference
156156
}
157157
}
158+
159+
/// Returns a read-only shared reference to the resulting value of a given closure.
160+
///
161+
/// - Returns: A new shared reader.
162+
public func map<Member>(
163+
_ body: @escaping @Sendable (Value) -> Member
164+
) -> SharedReader<Member> {
165+
func open(_ reference: some Reference<Value>) -> SharedReader<Member> {
166+
SharedReader<Member>(
167+
reference: _ReadClosureReference(base: reference, body: body)
168+
)
169+
}
170+
return open(reference)
171+
}
158172

159173
/// Returns a read-only shared reference to the resulting value of a given key path.
160174
///

Tests/SharingTests/EquatableTests.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,18 @@ struct EquatableTests {
3232
#expect(lhs == rhs)
3333
#expect($lhs == $rhs)
3434
}
35+
36+
@Test func map() {
37+
@Shared(value: 0) var base: Int
38+
@SharedReader var lhs: Int
39+
@SharedReader var rhs: Int
40+
_lhs = $base.map { $0 * 2 }
41+
_rhs = $base.map { $0 * 3 }
42+
#expect(lhs == rhs)
43+
#expect($lhs == $rhs)
44+
45+
$base.withLock { $0 += 1 }
46+
#expect(lhs != rhs)
47+
#expect($lhs != $rhs)
48+
}
3549
}

Tests/SharingTests/SharedTests.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,22 @@ import Testing
8787

8888
#expect(id == 42)
8989
}
90+
91+
@Test func map() {
92+
@Shared(value: 0) var count
93+
@SharedReader var isZero: Bool
94+
_isZero = $count.map { $0 == 0 }
95+
96+
#expect(isZero)
97+
98+
$count.withLock { $0 += 1 }
99+
100+
#expect(!isZero)
101+
102+
$count = Shared(value: 0)
103+
104+
#expect(!isZero)
105+
}
90106

91107
@Test func optional() throws {
92108
@Shared(value: nil) var wrappedCount: Int?

0 commit comments

Comments
 (0)