Skip to content

Commit 055a2d2

Browse files
author
pjechris
authored
[store] post nil when alias is removed (#67)
1 parent 86cac19 commit 055a2d2

File tree

5 files changed

+91
-9
lines changed

5 files changed

+91
-9
lines changed

Sources/CohesionKit/EntityStore.swift

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -349,25 +349,54 @@ extension EntityStore {
349349
extension EntityStore {
350350
/// Removes an alias from the storage
351351
public func removeAlias<T>(named: AliasKey<T>) {
352-
refAliases[named] = nil
353-
logger?.didUnregisterAlias(named)
352+
transaction {
353+
if let alias = refAliases[named] {
354+
do {
355+
try alias.updateEntity(AliasContainer(key: named, content: nil), modifiedAt: nil)
356+
logger?.didUnregisterAlias(named)
357+
}
358+
catch {
359+
360+
}
361+
}
362+
}
354363
}
355364

356365
/// Removes an alias from the storage
357366
public func removeAlias<C: Collection>(named: AliasKey<C>) {
358-
refAliases[named] = nil
359-
logger?.didUnregisterAlias(named)
367+
transaction {
368+
if let alias = refAliases[named] {
369+
do {
370+
try alias.updateEntity(AliasContainer(key: named, content: nil), modifiedAt: nil)
371+
logger?.didUnregisterAlias(named)
372+
}
373+
catch {
374+
375+
}
376+
}
377+
}
378+
360379
}
361380

362381
/// Removes all alias from identity map
363382
public func removeAllAlias() {
364-
refAliases.removeAll()
383+
transaction {
384+
removeAliases()
385+
}
365386
}
366387

367388
/// Removes all alias AND all objects stored weakly. You should not need this method and rather use `removeAlias`.
368389
/// But this can be useful if you fear retain cycles
369390
public func removeAll() {
370-
refAliases.removeAll()
371-
storage.removeAll()
391+
transaction {
392+
removeAliases()
393+
storage.removeAll()
394+
}
395+
}
396+
397+
private func removeAliases() {
398+
for (_, node) in refAliases {
399+
node.nullify()
400+
}
372401
}
373402
}

Sources/CohesionKit/Storage/AliasContainer.swift

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,4 +68,18 @@ extension AliasContainer: CollectionIdentifiableKeyPathsEraser where T: MutableC
6868
var erasedEntitiesKeyPaths: [Any] {
6969
[PartialIdentifiableKeyPath<Self>(\.content)]
7070
}
71-
}
71+
}
72+
73+
extension AliasContainer: Equatable where T: Equatable {
74+
75+
}
76+
77+
protocol Nullable {
78+
func nullified() -> Self
79+
}
80+
81+
extension AliasContainer: Nullable {
82+
func nullified() -> AliasContainer<T> {
83+
AliasContainer(key: key, content: nil)
84+
}
85+
}

Sources/CohesionKit/Storage/AliasStorage.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/// Keep a strong reference on each aliased node
2-
typealias AliasStorage = [String: Any]
2+
typealias AliasStorage = [String: AnyEntityNode]
33

44
extension AliasStorage {
55
subscript<T>(_ aliasKey: AliasKey<T>) -> EntityNode<AliasContainer<T>>? {

Sources/CohesionKit/Storage/EntityNode.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import Combine
44
/// Typed erased protocol
55
protocol AnyEntityNode: AnyObject {
66
var value: Any { get }
7+
8+
func nullify()
79
}
810

911
/// A graph node representing a entity of type `T` and its children. Anytime one of its children is updated the node
@@ -53,6 +55,12 @@ class EntityNode<T>: AnyEntityNode {
5355
onChange?(self)
5456
}
5557

58+
func nullify() {
59+
if let value = ref.value as? Nullable {
60+
try? updateEntity(value.nullified() as! T, modifiedAt: nil)
61+
}
62+
}
63+
5664
func removeAllChildren() {
5765
children = [:]
5866
}

Tests/CohesionKitTests/EntityStoreTests.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,37 @@ extension EntityStoreTests {
424424
}
425425
}
426426

427+
// MARK: Remove
428+
extension EntityStoreTests {
429+
func test_removeAlias_itEnqueuesNilInRegistry() {
430+
let registry = ObserverRegistryStub()
431+
let store = EntityStore(registry: registry)
432+
433+
_ = store.store(entity: SingleNodeFixture(id: 1), named: .test)
434+
435+
registry.clearPendingChangesStub()
436+
437+
store.removeAlias(named: .test)
438+
439+
XCTAssertTrue(registry.hasPendingChange(for: AliasContainer(key: .test, content: nil)))
440+
}
441+
442+
func test_removeAllAlias_itEnqueuesNilForEachAlias() {
443+
let registry = ObserverRegistryStub()
444+
let store = EntityStore(registry: registry)
445+
446+
_ = store.store(entity: SingleNodeFixture(id: 1), named: .test)
447+
_ = store.store(entities: [SingleNodeFixture(id: 2)], named: .listOfNodes)
448+
449+
registry.clearPendingChangesStub()
450+
451+
store.removeAllAlias()
452+
453+
XCTAssertTrue(registry.hasPendingChange(for: AliasContainer(key: .test, content: nil)))
454+
XCTAssertTrue(registry.hasPendingChange(for: AliasContainer(key: .listOfNodes, content: nil)))
455+
}
456+
}
457+
427458
private extension AliasKey where T == SingleNodeFixture {
428459
static let test = AliasKey(named: "test")
429460
}

0 commit comments

Comments
 (0)