11import Foundation
22import Combine
33
4+ struct EntityMetadata {
5+ /// children this entity is referencing/using
6+ // TODO: change key to a ObjectKey
7+ var childrenRefs : [ String : AnyKeyPath ] = [ : ]
8+
9+ /// parents referencing this entity. This means this entity should be listed inside its parents `EntityMetadata.childrenRefs` attribute
10+ // TODO: Change value to ObjectKey
11+ var parentsRefs : Set < String > = [ ]
12+ /// alias referencing this entity
13+ var aliasesRefs : Set < String > = [ ]
14+
15+ /// number of observers
16+ var observersCount : Int = 0
17+
18+ var isActivelyUsed : Bool {
19+ observersCount > 0 || !parentsRefs. isEmpty || !aliasesRefs. isEmpty
20+ }
21+ }
22+
423/// Typed erased protocol
524protocol AnyEntityNode : AnyObject {
25+ associatedtype Value
26+
27+ var ref : Observable < Value > { get }
628 var value : Any { get }
29+ var metadata : EntityMetadata { get }
30+ var storageKey : String { get }
731
8- func nullify( )
32+ func nullify( ) -> Bool
33+ func removeParent( _ node: any AnyEntityNode )
34+ func updateEntityRelationship( _ child: some AnyEntityNode )
35+ func enqueue( in: ObserverRegistry )
936}
1037
1138/// A graph node representing a entity of type `T` and its children. Anytime one of its children is updated the node
1239/// will reflect the change on its own value.
1340class EntityNode < T> : AnyEntityNode {
41+ typealias Value = T
1442 /// A child subscription used by its EntityNode parent
1543 struct SubscribedChild {
1644 /// the child subscription. Use it to unsubscribe to child upates
1745 let subscription : Subscription
1846 /// the child node value
19- let node : AnyEntityNode
47+ let node : any AnyEntityNode
2048 }
2149
2250 var value : Any { ref. value }
2351
52+ var metadata = EntityMetadata ( )
53+ // FIXME: to delete, it's "just" to have a strong ref and avoid nodes to be deleted. Need a better memory management
54+ private var childrenNodes : [ any AnyEntityNode ] = [ ]
55+
2456 var applyChildrenChanges = true
2557 /// An observable entity reference
2658 let ref : Observable < T >
2759
60+ let storageKey : String
61+
2862 private let onChange : ( ( EntityNode < T > ) -> Void ) ?
2963 /// last time the ref.value was changed. Any subsequent change must have a higher value to be applied
3064 /// if nil ref has no stamp and any change will be accepted
3165 private var modifiedAt : Stamp ?
3266 /// entity children
3367 private( set) var children : [ PartialKeyPath < T > : SubscribedChild ] = [ : ]
3468
35- init ( ref: Observable < T > , modifiedAt: Stamp ? , onChange: ( ( EntityNode < T > ) -> Void ) ? = nil ) {
69+ init ( ref: Observable < T > , key : String , modifiedAt: Stamp ? , onChange: ( ( EntityNode < T > ) -> Void ) ? = nil ) {
3670 self . ref = ref
3771 self . modifiedAt = modifiedAt
3872 self . onChange = onChange
73+ self . storageKey = key
74+ }
75+
76+ convenience init ( _ entity: T , key: String , modifiedAt: Stamp ? , onChange: ( ( EntityNode < T > ) -> Void ) ? = nil ) {
77+ self . init ( ref: Observable ( value: entity) , key: key, modifiedAt: modifiedAt, onChange: onChange)
3978 }
4079
41- convenience init ( _ entity: T , modifiedAt: Stamp ? , onChange: ( ( EntityNode < T > ) -> Void ) ? = nil ) {
42- self . init ( ref: Observable ( value: entity) , modifiedAt: modifiedAt, onChange: onChange)
80+ convenience init ( _ entity: T , modifiedAt: Stamp ? , onChange: ( ( EntityNode < T > ) -> Void ) ? = nil ) where T: Identifiable {
81+ let key = " \( T . self) - \( entity. id) "
82+ self . init ( entity, key: key, modifiedAt: modifiedAt, onChange: onChange)
4383 }
4484
4585 /// change the entity to a new value. If modifiedAt is nil or > to previous date update the value will be changed
@@ -52,17 +92,56 @@ class EntityNode<T>: AnyEntityNode {
5292
5393 modifiedAt = newModifiedAt ?? modifiedAt
5494 ref. value = newEntity
55- onChange ? ( self )
5695 }
5796
58- func nullify( ) {
97+ func nullify( ) -> Bool {
5998 if let value = ref. value as? Nullable {
60- try ? updateEntity ( value. nullified ( ) as! T , modifiedAt: nil )
99+ do {
100+ try updateEntity ( value. nullified ( ) as! T , modifiedAt: nil )
101+ return true
102+ }
103+ catch {
104+ return false
105+ }
61106 }
107+
108+ return false
62109 }
63110
64111 func removeAllChildren( ) {
65112 children = [ : ]
113+ metadata. childrenRefs = [ : ]
114+ childrenNodes = [ ]
115+ }
116+
117+ func removeParent( _ node: any AnyEntityNode ) {
118+ metadata. parentsRefs. remove ( node. storageKey)
119+ }
120+
121+ func updateEntityRelationship< U: AnyEntityNode > ( _ child: U ) {
122+ guard applyChildrenChanges else {
123+ return
124+ }
125+
126+ guard let keyPath = metadata. childrenRefs [ child. storageKey] else {
127+ return
128+ }
129+
130+ if let writableKeyPath = keyPath as? WritableKeyPath < T , U . Value > {
131+ ref. value [ keyPath: writableKeyPath] = child. ref. value
132+ return
133+ }
134+
135+ if let optionalWritableKeyPath = keyPath as? WritableKeyPath < T , U . Value ? > {
136+ ref. value [ keyPath: optionalWritableKeyPath] = child. ref. value
137+ return
138+ }
139+
140+ print ( " CohesionKit: cannot convert \( type ( of: keyPath) ) to WritableKeyPath< \( T . self) , \( U . Value. self) > " )
141+ }
142+
143+ func enqueue( in registry: ObserverRegistry ) {
144+ registry. enqueueChange ( for: self )
66145 }
67146
68147 /// observe one of the node child
@@ -88,20 +167,9 @@ class EntityNode<T>: AnyEntityNode {
88167 identity keyPath: KeyPath < T , C > ,
89168 update: @escaping ( inout T , Element ) -> Void
90169 ) {
91- if let subscribedChild = children [ keyPath] ? . node as? EntityNode < Element > , subscribedChild == childNode {
92- return
93- }
94-
95- let subscription = childNode. ref. addObserver { [ unowned self] newValue in
96- guard self . applyChildrenChanges else {
97- return
98- }
99-
100- update ( & self . ref. value, newValue)
101- self . onChange ? ( self )
102- }
103-
104- children [ keyPath] = SubscribedChild ( subscription: subscription, node: childNode)
170+ metadata. childrenRefs [ childNode. storageKey] = keyPath
171+ childNode. metadata. parentsRefs. insert ( storageKey)
172+ childrenNodes. append ( childNode)
105173 }
106174}
107175
0 commit comments