@@ -3,49 +3,60 @@ import Foundation
33/// Registers observers associated to an ``EntityNode``.
44/// The registry will handle notifying observers when a node is marked as changed
55class ObserverRegistry {
6- typealias Observer = ( Any ) -> Void
7- private typealias ObserverID = Int
86 private typealias Hash = Int
97
108 let queue : DispatchQueue
11- /// registered observers
12- private var observers : [ Hash : [ ObserverID : Observer ] ] = [ : ]
13- /// next available id for an observer
14- private var nextObserverID : ObserverID = 0
9+ /// registered observer handlers
10+ private var handlers : [ Hash : Set < Handler > ] = [ : ]
1511 /// nodes waiting for notifiying their observes about changes
1612 private var pendingChanges : [ Hash : AnyWeak ] = [ : ]
1713
1814 init ( queue: DispatchQueue ? = nil ) {
19- self . queue = queue ?? DispatchQueue ( label : " com.cohesionkit.registry " )
15+ self . queue = queue ?? DispatchQueue . main
2016 }
2117
2218 /// register an observer to observe changes on an entity node. Everytime `ObserverRegistry` is notified about changes
2319 /// to this node `onChange` will be called.
2420 func addObserver< T> ( node: EntityNode < T > , initial: Bool = false , onChange: @escaping ( T ) -> Void ) -> Subscription {
25- let observerID = generateID ( )
21+ let handler = Handler { onChange ( $0 . ref . value ) }
2622
27- observers [ node. hashValue, default: [ : ] ] [ observerID] = {
28- guard let newValue = $0 as? EntityNode < T > else {
29- return
23+ if initial {
24+ if queue == DispatchQueue . main && Thread . isMainThread {
25+ onChange ( node. ref. value)
26+ }
27+ else {
28+ queue. sync {
29+ onChange ( node. ref. value)
3030 }
31+ }
32+ }
3133
32- onChange ( newValue. ref. value)
34+ return subscribeHandler ( handler, for: node)
35+ }
36+
37+ /// Add an observer handler to multiple nodes.
38+ /// Note that the same handler will be added to each nodes. But it should get notified only once per transaction
39+ func addObserver< T> ( nodes: [ EntityNode < T > ] , initial: Bool = false , onChange: @escaping ( [ T ] ) -> Void ) -> Subscription {
40+ let handler = Handler { ( _: EntityNode < T > ) in
41+ // use last value from nodes
42+ onChange ( nodes. map ( \. ref. value) )
3343 }
3444
3545 if initial {
3646 if queue == DispatchQueue . main && Thread . isMainThread {
37- onChange ( node . ref. value)
47+ onChange ( nodes . map ( \ . ref. value) )
3848 }
3949 else {
4050 queue. sync {
41- onChange ( node . ref. value)
51+ onChange ( nodes . map ( \ . ref. value) )
4252 }
4353 }
4454 }
4555
46- // subscription keeps a strong ref to node, avoiding it from being released somehow while suscription is running
47- return Subscription { [ node] in
48- self . observers [ node. hashValue] ? . removeValue ( forKey: observerID)
56+ let subscriptions = nodes. map { node in subscribeHandler ( handler, for: node) }
57+
58+ return Subscription {
59+ subscriptions. forEach { $0. unsubscribe ( ) }
4960 }
5061 }
5162
@@ -61,26 +72,67 @@ class ObserverRegistry {
6172 /// Notify observers of all queued changes. Once notified pending changes are cleared out.
6273 func postChanges( ) {
6374 let changes = pendingChanges
64- let observers = self . observers
75+ let handlers = self . handlers
76+ var executedHandlers : Set < Handler > = [ ]
6577
6678 self . pendingChanges = [ : ]
6779
68- queue. async { [ unowned self ] in
80+ queue. async {
6981 for (hashKey, weakNode) in changes {
7082 // node was released: no one to notify
7183 guard let node = weakNode. unwrap ( ) else {
7284 continue
7385 }
7486
75- observers [ hashKey] ? . forEach { ( _, observer) in
76- observer ( node)
87+ for handler in handlers [ hashKey] ?? [ ] {
88+ // if some handlers are used multiple times (like for collections), make sure we don't notify them multiple times
89+ guard !executedHandlers. contains ( handler) else {
90+ continue
91+ }
92+
93+ handler ( node)
94+ executedHandlers. insert ( handler)
7795 }
7896 }
7997 }
8098 }
8199
82- private func generateID( ) -> ObserverID {
83- defer { nextObserverID &+= 1 }
84- return nextObserverID
100+ private func subscribeHandler< T> ( _ handler: Handler , for node: EntityNode < T > ) -> Subscription {
101+ handlers [ node. hashValue, default: [ ] ] . insert ( handler)
102+
103+ // subscription keeps a strong ref to node, avoiding it from being released somehow while suscription is running
104+ return Subscription { [ node] in
105+ self . handlers [ node. hashValue] ? . remove ( handler)
106+ }
85107 }
86108}
109+
110+ extension ObserverRegistry {
111+ /// Handle observation for a given node
112+ class Handler : Hashable {
113+ let executor : ( Any ) -> Void
114+
115+ init < T> ( executor: @escaping ( EntityNode < T > ) -> Void ) {
116+ self . executor = {
117+ guard let entity = $0 as? EntityNode < T > else {
118+ return
119+ }
120+
121+ executor ( entity)
122+ }
123+ }
124+
125+ /// execute the handler if `executeAtMost` does not exceed `executeCount`
126+ func callAsFunction( _ value: Any ) {
127+ executor ( value)
128+ }
129+
130+ static func == ( lhs: Handler , rhs: Handler ) -> Bool {
131+ ObjectIdentifier ( lhs) == ObjectIdentifier ( rhs)
132+ }
133+
134+ func hash( into hasher: inout Hasher ) {
135+ hasher. combine ( ObjectIdentifier ( self ) )
136+ }
137+ }
138+ }
0 commit comments