forked from pointfreeco/swift-sharing
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSharedKey.swift
More file actions
149 lines (137 loc) · 5.98 KB
/
SharedKey.swift
File metadata and controls
149 lines (137 loc) · 5.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import Dependencies
/// A type that can persist shared state to an external storage.
///
/// Conform to this protocol to express persistence to some external storage by describing how to
/// save to and load from the external storage, and providing a stream of values that represents
/// when the external storage is changed from the outside. It is only necessary to conform to this
/// protocol if the ``AppStorageKey``, ``FileStorageKey``, or ``InMemoryKey`` strategies are not
/// sufficient for your use case.
///
/// See the article <doc:PersistenceStrategies#Custom-persistence> for more information.
public protocol SharedKey<Value>: SharedReaderKey {
/// Saves a value to storage.
///
/// - Parameters:
/// - value: The value to save.
/// - context: The context of saving a value.
/// - continuation: A continuation that should be notified upon the completion of saving a
/// shared value.
func save(_ value: Value, context: SaveContext, continuation: SaveContinuation)
}
/// The context in which a value is saved by a ``SharedKey``.
///
/// A key may use this context to determine the behavior of the save. For example, an external
/// system that may be expensive to write to very frequently (_i.e._ network or file IO) could
/// choose to debounce saves when the value is simply updated in memory (via
/// ``Shared/withLock(_:fileID:filePath:line:column:)``), but forgo debouncing with an immediate
/// write when the value is saved explicitly (via ``Shared/save()``).
public enum SaveContext: Hashable, Sendable {
/// The value is being saved implicitly (after a mutation via
/// ``Shared/withLock(_:fileID:filePath:line:column:)``).
case didSet
/// The value is being saved explicitly (via ``Shared/save()``).
case userInitiated
}
extension Shared {
/// Creates a shared reference to a value using a shared key.
///
/// - Parameters:
/// - wrappedValue: A default value that is used when no value can be returned from the
/// shared key.
/// - key: A shared key associated with the shared reference. It is responsible for loading
/// and saving the shared reference's value from some external source.
public init(
wrappedValue: @autoclosure () -> Value,
_ key: some SharedKey<Value>
) {
@Dependency(PersistentReferences.self) var persistentReferences
self.init(rethrowing: wrappedValue(), key, skipInitialLoad: false)
}
/// Creates a shared reference to an optional value using a shared key.
///
/// - Parameter key: A shared key associated with the shared reference. It is responsible for
/// loading and saving the shared reference's value from some external source.
@_disfavoredOverload
public init<Wrapped>(_ key: some SharedKey<Value>) where Value == Wrapped? {
self.init(wrappedValue: nil, key)
}
/// Creates a shared reference to a value using a shared key with a default value.
///
/// - Parameter key: A shared key associated with the shared reference. It is responsible for
/// loading and saving the shared reference's value from some external source.
public init(_ key: (some SharedKey<Value>).Default) {
self.init(wrappedValue: key.initialValue, key.base)
}
/// Creates a shared reference to a value using a shared key by overriding its default value.
///
/// - Parameters:
/// - wrappedValue: A default value that is used when no value can be returned from the
/// shared key.
/// - key: A shared key associated with the shared reference. It is responsible for loading
/// and saving the shared reference's value from some external source.
public init(
wrappedValue: @autoclosure () -> Value,
_ key: (some SharedKey<Value>).Default
) {
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 and saving the shared reference's value from some external source.
public func load(_ key: some SharedKey<Value>) async throws {
@Dependency(PersistentReferences.self) var persistentReferences
SharedPublisherLocals.$isLoading.withValue(true) {
projectedValue = Shared(
reference: persistentReferences.value(
forKey: key,
default: wrappedValue,
skipInitialLoad: true
)
)
}
try await load()
}
/// Creates a shared reference to a value using a shared key by loading it from its external
/// source.
///
/// If the given shared key cannot load a value, an error is thrown. For a non-throwing,
/// synchronous version of this initializer, see ``init(wrappedValue:_:)-5xce4``.
///
/// This initializer should only be used to create a brand new shared reference from a key. To
/// replace the key of an existing shared reference, use ``load(_:)``, instead.
///
/// - Parameter key: A shared key associated with the shared reference. It is responsible for
/// loading and saving the shared reference's value from some external source.
public init(require key: some SharedKey<Value>) async throws {
let value = try await withUnsafeThrowingContinuation { continuation in
key.load(
context: .userInitiated,
continuation: LoadContinuation { result in
continuation.resume(with: result)
}
)
}
guard let value else { throw LoadError() }
self.init(rethrowing: value, key, skipInitialLoad: true)
if let loadError { throw loadError }
}
@available(*, unavailable, message: "Assign a default value")
public init(_ key: some SharedKey<Value>) {
fatalError()
}
private init(
rethrowing value: @autoclosure () throws -> Value, _ key: some SharedKey<Value>,
skipInitialLoad: Bool
) rethrows {
@Dependency(PersistentReferences.self) var persistentReferences
self.init(
reference: try persistentReferences.value(
forKey: key,
default: try value(),
skipInitialLoad: skipInitialLoad
)
)
}
private struct LoadError: Error {}
}