Skip to content

Commit 774f51b

Browse files
authored
feat(keyPath): Use WritableKeyPath instead of KeyPath (#50)
1 parent 0e13841 commit 774f51b

File tree

12 files changed

+80
-216
lines changed

12 files changed

+80
-216
lines changed

Example/Example/Model.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ struct Outcome: Identifiable {
2929
struct MatchMarkets: Aggregate {
3030
var id: Match.ID { match.id }
3131

32-
let match: Match
33-
let markets: [MarketOutcomes]
32+
var match: Match
33+
var markets: [MarketOutcomes]
3434

3535
var primaryMarket: MarketOutcomes {
3636
// this is a sample project, we just consider that there is always at least one market
@@ -46,8 +46,8 @@ struct MatchMarkets: Aggregate {
4646
struct MarketOutcomes: Aggregate {
4747
var id: Market.ID { market.id }
4848

49-
let market: Market
50-
let outcomes: [Outcome]
49+
var market: Market
50+
var outcomes: [Outcome]
5151

5252
var nestedEntitiesKeyPaths: [PartialIdentifiableKeyPath<Self>] {[
5353
.init(\.market),

Sources/CohesionKit/KeyPath/PartialIdentifiableKeyPath.swift

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ import Combine
44
public struct PartialIdentifiableKeyPath<Root> {
55
let keyPath: PartialKeyPath<Root>
66
let accept: (EntityNode<Root>, Root, Stamp, NestedEntitiesVisitor) -> Void
7-
8-
public init<T: Identifiable>(_ keyPath: KeyPath<Root, T>) {
7+
8+
/// Creates an instance referencing an `Identifiable` keyPath
9+
public init<T: Identifiable>(_ keyPath: WritableKeyPath<Root, T>) {
910
self.keyPath = keyPath
1011
self.accept = { parent, root, stamp, visitor in
1112
visitor.visit(
@@ -14,8 +15,9 @@ public struct PartialIdentifiableKeyPath<Root> {
1415
)
1516
}
1617
}
17-
18-
public init<T: Aggregate>(_ keyPath: KeyPath<Root, T>) {
18+
19+
/// Creates an instance referencing an `Aggregate` keyPath
20+
public init<T: Aggregate>(_ keyPath: WritableKeyPath<Root, T>) {
1921
self.keyPath = keyPath
2022
self.accept = { parent, root, stamp, visitor in
2123
visitor.visit(
@@ -24,8 +26,9 @@ public struct PartialIdentifiableKeyPath<Root> {
2426
)
2527
}
2628
}
27-
28-
public init<T: Identifiable>(_ keyPath: KeyPath<Root, T?>) {
29+
30+
/// Creates an instance referencing an optional `Identifiable` keyPath
31+
public init<T: Identifiable>(_ keyPath: WritableKeyPath<Root, T?>) {
2932
self.keyPath = keyPath
3033
self.accept = { parent, root, stamp, visitor in
3134
visitor.visit(
@@ -34,8 +37,8 @@ public struct PartialIdentifiableKeyPath<Root> {
3437
)
3538
}
3639
}
37-
38-
public init<T: Aggregate>(_ keyPath: KeyPath<Root, T?>) {
40+
41+
public init<T: Aggregate>(_ keyPath: WritableKeyPath<Root, T?>) {
3942
self.keyPath = keyPath
4043
self.accept = { parent, root, stamp, visitor in
4144
visitor.visit(
@@ -44,8 +47,8 @@ public struct PartialIdentifiableKeyPath<Root> {
4447
)
4548
}
4649
}
47-
48-
public init<C: BufferedCollection>(_ keyPath: KeyPath<Root, C>) where C.Element: Identifiable, C.Index: Hashable {
50+
51+
public init<C: MutableCollection>(_ keyPath: WritableKeyPath<Root, C>) where C.Element: Identifiable, C.Index: Hashable {
4952
self.keyPath = keyPath
5053
self.accept = { parent, root, stamp, visitor in
5154
visitor.visit(
@@ -54,8 +57,8 @@ public struct PartialIdentifiableKeyPath<Root> {
5457
)
5558
}
5659
}
57-
58-
public init<C: BufferedCollection>(_ keyPath: KeyPath<Root, C>) where C.Element: Aggregate, C.Index: Hashable {
60+
61+
public init<C: MutableCollection>(_ keyPath: WritableKeyPath<Root, C>) where C.Element: Aggregate, C.Index: Hashable {
5962
self.keyPath = keyPath
6063
self.accept = { parent, root, stamp, visitor in
6164
visitor.visit(

Sources/CohesionKit/Storage/EntityNode.swift

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -54,35 +54,27 @@ class EntityNode<T>: AnyEntityNode {
5454
}
5555

5656
/// observe one of the node child
57-
func observeChild<C>(_ childNode: EntityNode<C>, for keyPath: KeyPath<T, C>) {
58-
observeChild(childNode, identity: keyPath) { pointer, newValue in
59-
pointer.assign(newValue, to: keyPath)
57+
func observeChild<C>(_ childNode: EntityNode<C>, for keyPath: WritableKeyPath<T, C>) {
58+
observeChild(childNode, identity: keyPath) { root, newValue in
59+
root[keyPath: keyPath] = newValue
6060
}
6161
}
6262

6363
/// observe a non nil child but whose keypath is represented by an Optional
64-
func observeChild<C>(_ childNode: EntityNode<C>, for keyPath: KeyPath<T, C?>) {
65-
observeChild(childNode, identity: keyPath) { pointer, newValue in
66-
pointer.assign(.some(newValue), to: keyPath)
67-
}
68-
}
69-
70-
/// observe one of the node child whose type is a collection
71-
func observeChild<C: BufferedCollection>(_ childNode: EntityNode<C.Element>, for keyPath: KeyPath<T, C>, index: C.Index)
72-
where C.Index: Hashable {
73-
observeChild(childNode, identity: keyPath.appending(path: \C[index])) { pointer, newValue in
74-
pointer.assign(newValue, to: keyPath, index: index)
64+
func observeChild<C>(_ childNode: EntityNode<C>, for keyPath: WritableKeyPath<T, C?>) {
65+
observeChild(childNode, identity: keyPath) { root, newValue in
66+
root[keyPath: keyPath] = .some(newValue)
7567
}
7668
}
7769

7870
/// Observe a node child
7971
/// - Parameter childNode: the child to observe
80-
/// - Parameter keyPath: a **unique** keypath associated to the child. Should have similar type but maybe a little different (optional, Array.Element, ...)
72+
/// - Parameter keyPath: a **unique** keypath associated to the child. Should have similar type but maybe a little different (optional)
8173
/// - Parameter assign: to assign childNode value to current node ref value
8274
private func observeChild<C, Element>(
8375
_ childNode: EntityNode<Element>,
8476
identity keyPath: KeyPath<T, C>,
85-
update: @escaping (UnsafeMutablePointer<T>, Element) -> Void
77+
update: @escaping (inout T, Element) -> Void
8678
) {
8779
if let subscribedChild = children[keyPath]?.node as? EntityNode<Element>, subscribedChild == childNode {
8880
return
@@ -93,14 +85,11 @@ class EntityNode<T>: AnyEntityNode {
9385
return
9486
}
9587

96-
withUnsafeMutablePointer(to: &self.ref.value) {
97-
update($0, newValue)
98-
}
88+
update(&self.ref.value, newValue)
9989
}
10090

10191
children[keyPath] = SubscribedChild(subscription: subscription, node: childNode)
10292
}
103-
10493
}
10594

10695
extension EntityNode: Hashable {

Sources/CohesionKit/UnsafePointer/BufferedCollection.swift

Lines changed: 0 additions & 14 deletions
This file was deleted.

Sources/CohesionKit/UnsafePointer/UnsafeMutablePointer+KeyPath.swift

Lines changed: 0 additions & 27 deletions
This file was deleted.

Sources/CohesionKit/Visitor/EntityContext.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@ import Foundation
33
/// Information related to an entity while storing it into the `IdentityMap`
44
struct EntityContext<Root, Value> {
55
let parent: EntityNode<Root>
6-
let keyPath: KeyPath<Root, Value>
6+
let keyPath: WritableKeyPath<Root, Value>
77
let stamp: Stamp
88
}

Sources/CohesionKit/Visitor/IdentityMapStoreVisitor.swift

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,55 +3,57 @@ import Foundation
33
/// Visitor storing entity nested keypaths into IdentityMap
44
struct IdentityMapStoreVisitor: NestedEntitiesVisitor {
55
let identityMap: IdentityMap
6-
6+
77
func visit<Root, T: Identifiable>(context: EntityContext<Root, T>, entity: T) {
8+
let storedChild = identityMap.nodeStore(entity: entity, modifiedAt: context.stamp)
9+
810
context
911
.parent
10-
.observeChild(identityMap.nodeStore(entity: entity, modifiedAt: context.stamp), for: context.keyPath)
12+
.observeChild(storedChild, for: context.keyPath)
1113
}
12-
14+
1315
func visit<Root, T: Aggregate>(context: EntityContext<Root, T>, entity: T) {
16+
let storedChild = identityMap.nodeStore(entity: entity, modifiedAt: context.stamp)
17+
1418
context
1519
.parent
16-
.observeChild(identityMap.nodeStore(entity: entity, modifiedAt: context.stamp), for: context.keyPath)
20+
.observeChild(storedChild, for: context.keyPath)
1721
}
18-
22+
1923
func visit<Root, T: Identifiable>(context: EntityContext<Root, T?>, entity: T?) {
2024
if let entity = entity {
2125
context
2226
.parent
2327
.observeChild(identityMap.nodeStore(entity: entity, modifiedAt: context.stamp), for: context.keyPath)
2428
}
2529
}
26-
30+
2731
func visit<Root, T: Aggregate>(context: EntityContext<Root, T?>, entity: T?) {
2832
if let entity = entity {
2933
context
3034
.parent
3135
.observeChild(identityMap.nodeStore(entity: entity, modifiedAt: context.stamp), for: context.keyPath)
3236
}
3337
}
34-
35-
func visit<Root, C: BufferedCollection>(context: EntityContext<Root, C>, entities: C)
38+
39+
func visit<Root, C: MutableCollection>(context: EntityContext<Root, C>, entities: C)
3640
where C.Element: Identifiable, C.Index: Hashable {
37-
41+
3842
for index in entities.indices {
3943
context.parent.observeChild(
4044
identityMap.nodeStore(entity: entities[index], modifiedAt: context.stamp),
41-
for: context.keyPath,
42-
index: index
45+
for: context.keyPath.appending(path: \.[index])
4346
)
4447
}
4548
}
46-
47-
func visit<Root, C: BufferedCollection>(context: EntityContext<Root, C>, entities: C)
49+
50+
func visit<Root, C: MutableCollection>(context: EntityContext<Root, C>, entities: C)
4851
where C.Element: Aggregate, C.Index: Hashable {
49-
52+
5053
for index in entities.indices {
5154
context.parent.observeChild(
5255
identityMap.nodeStore(entity: entities[index], modifiedAt: context.stamp),
53-
for: context.keyPath,
54-
index: index
56+
for: context.keyPath.appending(path: \.[index])
5557
)
5658
}
5759
}

Sources/CohesionKit/Visitor/NestedEntitiesVisitor.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ import Foundation
44
protocol NestedEntitiesVisitor {
55
func visit<Root, T: Identifiable>(context: EntityContext<Root, T>, entity: T)
66
func visit<Root, T: Aggregate>(context: EntityContext<Root, T>, entity: T)
7-
7+
88
func visit<Root, T: Identifiable>(context: EntityContext<Root, T?>, entity: T?)
99
func visit<Root, T: Aggregate>(context: EntityContext<Root, T?>, entity: T?)
10-
11-
func visit<Root, C: BufferedCollection>(context: EntityContext<Root, C>, entities: C)
10+
11+
func visit<Root, C: MutableCollection>(context: EntityContext<Root, C>, entities: C)
1212
where C.Element: Identifiable, C.Index: Hashable
13-
14-
func visit<Root, C: BufferedCollection>(context: EntityContext<Root, C>, entities: C)
13+
14+
func visit<Root, C: MutableCollection>(context: EntityContext<Root, C>, entities: C)
1515
where C.Element: Aggregate, C.Index: Hashable
1616
}

Tests/CohesionKitTests/IdentityMapTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ class IdentityMapTests: XCTestCase {
7474
let identityMap = IdentityMap()
7575
let expectation = XCTestExpectation()
7676

77-
_ = withExtendedLifetime(identityMap.store(entity: root)) {
77+
withExtendedLifetime(identityMap.store(entity: root)) {
7878
_ = identityMap.store(entity: root, ifPresent: { _ in
7979
expectation.fulfill()
8080
})

Tests/CohesionKitTests/Storage/EntityNodeTests.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,15 +78,15 @@ class EntityNodeTests: XCTestCase {
7878
XCTAssertTrue(observerCalled)
7979
}
8080

81-
func test_observeChildIndex_eachChildIsAdded() {
81+
func test_observeChild_childIsCollection_eachChildIsAdded() {
8282
let child1 = EntityNode(ListNodeFixture(id: 1), modifiedAt: startTimestamp)
8383
let child2 = EntityNode(ListNodeFixture(id: 2), modifiedAt: startTimestamp)
8484
let node = EntityNode(startEntity, modifiedAt: startTimestamp)
8585

8686
XCTAssertEqual(node.children.count, 0)
8787

88-
node.observeChild(child1, for: \.listNodes, index: 0)
89-
node.observeChild(child2, for: \.listNodes, index: 1)
88+
node.observeChild(child1, for: \.listNodes[0])
89+
node.observeChild(child2, for: \.listNodes[1])
9090

9191
XCTAssertEqual(node.children.count, 2)
9292
}

0 commit comments

Comments
 (0)