9
9
//
10
10
//===----------------------------------------------------------------------===//
11
11
12
+ /// Provides storage for tracking and access to data changes.
13
+ ///
14
+ /// You don't need to create an instance of `ObservationRegistrar` when using
15
+ /// the ``Observation/Observable-swift.macro`` macro to indicate observability
16
+ /// of a type.
12
17
@available ( SwiftStdlib 5 . 9 , * )
13
18
public struct ObservationRegistrar : Sendable {
14
- struct State : @unchecked Sendable {
15
- struct Observation {
16
- var properties : Set < AnyKeyPath >
17
- var observer : @Sendable ( ) -> Void
19
+ internal class ValueObservationStorage {
20
+ func emit< Element> ( _ element: Element ) -> Bool { return false }
21
+ func cancel( ) { }
22
+ }
23
+
24
+ private struct ValuesObserver {
25
+ private let storage : ValueObservationStorage
26
+
27
+ internal init ( storage: ValueObservationStorage ) {
28
+ self . storage = storage
29
+ }
30
+
31
+ internal func emit< Element> ( _ element: Element ) -> Bool {
32
+ storage. emit ( element)
33
+ }
34
+
35
+ internal func cancel( ) {
36
+ storage. cancel ( )
37
+ }
38
+ }
39
+
40
+ private struct State : @unchecked Sendable {
41
+ private enum ObservationKind {
42
+ case willSetTracking( @Sendable ( ) -> Void )
43
+ case didSetTracking( @Sendable ( ) -> Void )
44
+ case computed( @Sendable ( Any ) -> Void )
45
+ case values( ValuesObserver )
46
+ }
47
+
48
+ private struct Observation {
49
+ private var kind : ObservationKind
50
+ internal var properties : Set < AnyKeyPath >
51
+
52
+ internal init ( kind: ObservationKind , properties: Set < AnyKeyPath > ) {
53
+ self . kind = kind
54
+ self . properties = properties
55
+ }
56
+
57
+ var willSetTracker : ( @Sendable ( ) -> Void ) ? {
58
+ switch kind {
59
+ case . willSetTracking( let tracker) :
60
+ return tracker
61
+ default :
62
+ return nil
63
+ }
64
+ }
65
+
66
+ var didSetTracker : ( @Sendable ( ) -> Void ) ? {
67
+ switch kind {
68
+ case . didSetTracking( let tracker) :
69
+ return tracker
70
+ default :
71
+ return nil
72
+ }
73
+ }
74
+
75
+ var observer : ( @Sendable ( Any ) -> Void ) ? {
76
+ switch kind {
77
+ case . computed( let observer) :
78
+ return observer
79
+ default :
80
+ return nil
81
+ }
82
+ }
83
+
84
+ var isValueObserver : Bool {
85
+ switch kind {
86
+ case . values:
87
+ return true
88
+ default :
89
+ return false
90
+ }
91
+ }
92
+
93
+ func emit< Element> ( _ value: Element ) -> Bool {
94
+ switch kind {
95
+ case . values( let observer) :
96
+ return observer. emit ( value)
97
+ default :
98
+ return false
99
+ }
100
+ }
101
+
102
+ func cancel( ) {
103
+ switch kind {
104
+ case . values( let observer) :
105
+ observer. cancel ( )
106
+ default :
107
+ break
108
+ }
109
+ }
18
110
}
19
111
20
- var id = 0
21
- var observations = [ Int : Observation] ( )
22
- var lookups = [ AnyKeyPath : Set < Int > ] ( )
112
+ private var id = 0
113
+ private var observations = [ Int : Observation] ( )
114
+ private var lookups = [ AnyKeyPath : Set < Int > ] ( )
23
115
24
- mutating func generateId( ) -> Int {
116
+ internal mutating func generateId( ) -> Int {
25
117
defer { id &+= 1 }
26
118
return id
27
119
}
28
120
29
- mutating func registerTracking( for properties: Set < AnyKeyPath > , observer: @Sendable @escaping ( ) -> Void ) -> Int {
121
+ internal mutating func registerTracking( for properties: Set < AnyKeyPath > , willSet observer: @Sendable @escaping ( ) -> Void ) -> Int {
122
+ let id = generateId ( )
123
+ observations [ id] = Observation ( kind: . willSetTracking( observer) , properties: properties)
124
+ for keyPath in properties {
125
+ lookups [ keyPath, default: [ ] ] . insert ( id)
126
+ }
127
+ return id
128
+ }
129
+
130
+ internal mutating func registerTracking( for properties: Set < AnyKeyPath > , didSet observer: @Sendable @escaping ( ) -> Void ) -> Int {
131
+ let id = generateId ( )
132
+ observations [ id] = Observation ( kind: . didSetTracking( observer) , properties: properties)
133
+ for keyPath in properties {
134
+ lookups [ keyPath, default: [ ] ] . insert ( id)
135
+ }
136
+ return id
137
+ }
138
+
139
+ internal mutating func registerComputedValues( for properties: Set < AnyKeyPath > , observer: @Sendable @escaping ( Any ) -> Void ) -> Int {
140
+ let id = generateId ( )
141
+ observations [ id] = Observation ( kind: . computed( observer) , properties: properties)
142
+ for keyPath in properties {
143
+ lookups [ keyPath, default: [ ] ] . insert ( id)
144
+ }
145
+ return id
146
+ }
147
+
148
+ internal mutating func registerValues( for properties: Set < AnyKeyPath > , storage: ValueObservationStorage ) -> Int {
30
149
let id = generateId ( )
31
- observations [ id] = Observation ( properties : properties , observer : observer )
150
+ observations [ id] = Observation ( kind : . values ( ValuesObserver ( storage : storage ) ) , properties : properties )
32
151
for keyPath in properties {
33
152
lookups [ keyPath, default: [ ] ] . insert ( id)
34
153
}
35
154
return id
36
155
}
37
156
38
- mutating func cancel( _ id: Int ) {
39
- if let tracking = observations. removeValue ( forKey: id) {
40
- for keyPath in tracking. properties {
157
+ internal func valueObservers( for keyPath: AnyKeyPath ) -> Set < Int > {
158
+ guard let ids = lookups [ keyPath] else {
159
+ return [ ]
160
+ }
161
+ return ids. filter { observations [ $0] ? . isValueObserver == true }
162
+ }
163
+
164
+ internal mutating func cancel( _ id: Int ) {
165
+ if let observation = observations. removeValue ( forKey: id) {
166
+ for keyPath in observation. properties {
41
167
if var ids = lookups [ keyPath] {
42
168
ids. remove ( id)
43
169
if ids. count == 0 {
@@ -47,52 +173,144 @@ public struct ObservationRegistrar: Sendable {
47
173
}
48
174
}
49
175
}
176
+ observation. cancel ( )
177
+ }
178
+ }
179
+
180
+ internal mutating func cancelAll( ) {
181
+ for observation in observations. values {
182
+ observation. cancel ( )
183
+ }
184
+ observations. removeAll ( )
185
+ lookups. removeAll ( )
186
+ }
187
+
188
+ internal mutating func willSet( keyPath: AnyKeyPath ) -> [ @Sendable ( ) -> Void ] {
189
+ var trackers = [ @Sendable ( ) -> Void ] ( )
190
+ if let ids = lookups [ keyPath] {
191
+ for id in ids {
192
+ if let tracker = observations [ id] ? . willSetTracker {
193
+ trackers. append ( tracker)
194
+ }
195
+ }
50
196
}
197
+ return trackers
51
198
}
52
199
53
- mutating func willSet( keyPath: AnyKeyPath ) -> [ @Sendable ( ) -> Void ] {
54
- var observers = [ @Sendable ( ) -> Void ] ( )
200
+ internal mutating func didSet< Subject: Observable , Member> ( keyPath: KeyPath < Subject , Member > ) -> ( [ @Sendable ( Any ) -> Void ] , [ @Sendable ( ) -> Void ] ) {
201
+ var observers = [ @Sendable ( Any ) -> Void ] ( )
202
+ var trackers = [ @Sendable ( ) -> Void ] ( )
55
203
if let ids = lookups [ keyPath] {
56
204
for id in ids {
57
- if let observation = observations [ id] {
58
- observers. append ( observation . observer)
205
+ if let observer = observations [ id] ? . observer {
206
+ observers. append ( observer)
59
207
cancel ( id)
60
208
}
209
+ if let tracker = observations [ id] ? . didSetTracker {
210
+ trackers. append ( tracker)
211
+ }
212
+ }
213
+ }
214
+ return ( observers, trackers)
215
+ }
216
+
217
+ internal mutating func emit< Element> ( _ value: Element , ids: Set < Int > ) {
218
+ for id in ids {
219
+ if observations [ id] ? . emit ( value) == true {
220
+ cancel ( id)
61
221
}
62
222
}
63
- return observers
64
223
}
65
224
}
66
225
67
- struct Context : Sendable {
68
- let state = _ManagedCriticalState ( State ( ) )
226
+ internal struct Context : Sendable {
227
+ private let state = _ManagedCriticalState ( State ( ) )
228
+
229
+ internal var id : ObjectIdentifier { state. id }
69
230
70
- var id : ObjectIdentifier { state. id }
231
+ internal func registerTracking( for properties: Set < AnyKeyPath > , willSet observer: @Sendable @escaping ( ) -> Void ) -> Int {
232
+ state. withCriticalRegion { $0. registerTracking ( for: properties, willSet: observer) }
233
+ }
234
+
235
+ internal func registerTracking( for properties: Set < AnyKeyPath > , didSet observer: @Sendable @escaping ( ) -> Void ) -> Int {
236
+ state. withCriticalRegion { $0. registerTracking ( for: properties, didSet: observer) }
237
+ }
71
238
72
- func registerTracking ( for properties: Set < AnyKeyPath > , observer: @Sendable @escaping ( ) -> Void ) -> Int {
73
- state. withCriticalRegion { $0. registerTracking ( for: properties, observer: observer) }
239
+ internal func registerComputedValues ( for properties: Set < AnyKeyPath > , observer: @Sendable @escaping ( Any ) -> Void ) -> Int {
240
+ state. withCriticalRegion { $0. registerComputedValues ( for: properties, observer: observer) }
74
241
}
75
242
76
- func cancel( _ id: Int ) {
243
+ internal func registerValues( for properties: Set < AnyKeyPath > , storage: ValueObservationStorage ) -> Int {
244
+ state. withCriticalRegion { $0. registerValues ( for: properties, storage: storage) }
245
+ }
246
+
247
+ internal func cancel( _ id: Int ) {
77
248
state. withCriticalRegion { $0. cancel ( id) }
78
249
}
250
+
251
+ internal func cancelAll( ) {
252
+ state. withCriticalRegion { $0. cancelAll ( ) }
253
+ }
79
254
80
- func willSet< Subject, Member> (
255
+ internal func willSet< Subject: Observable , Member> (
81
256
_ subject: Subject ,
82
257
keyPath: KeyPath < Subject , Member >
83
258
) {
84
- let actions = state. withCriticalRegion { $0. willSet ( keyPath: keyPath) }
85
- for action in actions {
259
+ let tracking = state. withCriticalRegion { $0. willSet ( keyPath: keyPath) }
260
+ for action in tracking {
261
+ action ( )
262
+ }
263
+ }
264
+
265
+ internal func didSet< Subject: Observable , Member> (
266
+ _ subject: Subject ,
267
+ keyPath: KeyPath < Subject , Member >
268
+ ) {
269
+ let ( ids, ( actions, tracking) ) = state. withCriticalRegion { ( $0. valueObservers ( for: keyPath) , $0. didSet ( keyPath: keyPath) ) }
270
+ if !ids. isEmpty {
271
+ let value = subject [ keyPath: keyPath]
272
+ state. withCriticalRegion { $0. emit ( value, ids: ids) }
273
+ }
274
+ for action in tracking {
86
275
action ( )
87
276
}
277
+ for action in actions {
278
+ action ( subject)
279
+ }
280
+ }
281
+ }
282
+
283
+ private final class Extent : @unchecked Sendable {
284
+ let context = Context ( )
285
+
286
+ init ( ) {
287
+ }
288
+
289
+ deinit {
290
+ context. cancelAll ( )
88
291
}
89
292
}
90
293
91
- let context = Context ( )
294
+ internal var context : Context {
295
+ return extent. context
296
+ }
92
297
298
+ private var extent = Extent ( )
299
+
300
+ /// Creates an instance of the observation registrar.
301
+ ///
302
+ /// You don't need to create an instance of
303
+ /// ``Observation/ObservationRegistrar`` when using the
304
+ /// ``Observation/Observable-swift.macro`` macro to indicate observably
305
+ /// of a type.
93
306
public init ( ) {
94
307
}
95
308
309
+ /// Registers access to a specific property for observation.
310
+ ///
311
+ /// - Parameters:
312
+ /// - subject: An instance of an observable type.
313
+ /// - keyPath: The key path of an observed property.
96
314
public func access< Subject: Observable , Member> (
97
315
_ subject: Subject ,
98
316
keyPath: KeyPath < Subject , Member >
@@ -106,20 +324,37 @@ public struct ObservationRegistrar: Sendable {
106
324
}
107
325
}
108
326
327
+ /// A property observation called before setting the value of the subject.
328
+ ///
329
+ /// - Parameters:
330
+ /// - subject: An instance of an observable type.
331
+ /// - keyPath: The key path of an observed property.
109
332
public func willSet< Subject: Observable , Member> (
110
333
_ subject: Subject ,
111
334
keyPath: KeyPath < Subject , Member >
112
335
) {
113
336
context. willSet ( subject, keyPath: keyPath)
114
337
}
115
-
338
+
339
+ /// A property observation called after setting the value of the subject.
340
+ ///
341
+ /// - Parameters:
342
+ /// - subject: An instance of an observable type.
343
+ /// - keyPath: The key path of an observed property.
116
344
public func didSet< Subject: Observable , Member> (
117
345
_ subject: Subject ,
118
346
keyPath: KeyPath < Subject , Member >
119
347
) {
120
-
348
+ context . didSet ( subject , keyPath : keyPath )
121
349
}
122
350
351
+ /// Identifies mutations to the transactions registered for observers.
352
+ ///
353
+ /// This method calls ``willset(_:keypath:)`` before the mutation. Then it
354
+ /// calls ``didset(_:keypath:)`` after the mutation.
355
+ /// - Parameters:
356
+ /// - of: An instance of an observable type.
357
+ /// - keyPath: The key path of an observed property.
123
358
public func withMutation< Subject: Observable , Member, T> (
124
359
of subject: Subject ,
125
360
keyPath: KeyPath < Subject , Member > ,
0 commit comments