Skip to content

Commit

Permalink
tech(Observable) Remove observable object (#74)
Browse files Browse the repository at this point in the history
  • Loading branch information
pjechris authored Jan 3, 2025
1 parent 6bb6cb5 commit 7e96304
Show file tree
Hide file tree
Showing 11 changed files with 63 additions and 129 deletions.
10 changes: 10 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
root = true

[*]
end_of_line = lf
trim_trailing_whitespace = true

# 4 space indentation
[*.swift]
indent_style = space
indent_size = 4
12 changes: 6 additions & 6 deletions Sources/CohesionKit/EntityStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ extension EntityStore {
@discardableResult
public func update<T: Identifiable>(_ type: T.Type, id: T.ID, modifiedAt: Stamp? = nil, update: Update<T>) -> Bool {
transaction {
guard var entity = storage[T.self, id: id]?.ref.value else {
guard var entity = storage[T.self, id: id]?.value else {
return false
}

Expand All @@ -275,7 +275,7 @@ extension EntityStore {
@discardableResult
public func update<T: Aggregate>(_ type: T.Type, id: T.ID, modifiedAt: Stamp? = nil, _ update: Update<T>) -> Bool {
transaction {
guard var entity = storage[T.self, id: id]?.ref.value else {
guard var entity = storage[T.self, id: id]?.value else {
return false
}

Expand All @@ -294,7 +294,7 @@ extension EntityStore {
@discardableResult
public func update<T: Identifiable>(named: AliasKey<T>, modifiedAt: Stamp? = nil, update: Update<T>) -> Bool {
transaction {
guard let aliasNode = refAliases[named], var content = aliasNode.ref.value.content else {
guard let aliasNode = refAliases[named], var content = aliasNode.value.content else {
return false
}

Expand All @@ -313,7 +313,7 @@ extension EntityStore {
@discardableResult
public func update<T: Aggregate>(named: AliasKey<T>, modifiedAt: Stamp? = nil, update: Update<T>) -> Bool {
transaction {
guard let aliasNode = refAliases[named], var content = aliasNode.ref.value.content else {
guard let aliasNode = refAliases[named], var content = aliasNode.value.content else {
return false
}

Expand All @@ -333,7 +333,7 @@ extension EntityStore {
public func update<C: Collection>(named: AliasKey<C>, modifiedAt: Stamp? = nil, update: Update<C>)
-> Bool where C.Element: Identifiable {
transaction {
guard let aliasNode = refAliases[named], var content = aliasNode.ref.value.content else {
guard let aliasNode = refAliases[named], var content = aliasNode.value.content else {
return false
}

Expand All @@ -353,7 +353,7 @@ extension EntityStore {
public func update<C: Collection>(named: AliasKey<C>, modifiedAt: Stamp? = nil, update: Update<C>)
-> Bool where C.Element: Aggregate {
transaction {
guard let aliasNode = refAliases[named], var content = aliasNode.ref.value.content else {
guard let aliasNode = refAliases[named], var content = aliasNode.value.content else {
return false
}

Expand Down
6 changes: 3 additions & 3 deletions Sources/CohesionKit/Observer/EntityObserver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,22 @@ public struct EntityObserver<T> {
let createObserver: (@escaping OnChange) -> Subscription

init(node: EntityNode<T>, registry: ObserverRegistry) {
self.value = node.ref.value
self.value = node.value
self.createObserver = { onChange in
registry.addObserver(node: node, initial: true, onChange: onChange)
}
}

init<Element>(nodes: [EntityNode<Element>], registry: ObserverRegistry) where T == [Element] {
self.value = nodes.map(\.ref.value)
self.value = nodes.map(\.value)
self.createObserver = { onChange in
registry.addObserver(nodes: nodes, initial: true, onChange: onChange)
}
}

init<Wrapped>(alias node: EntityNode<AliasContainer<Wrapped>>, registry: ObserverRegistry)
where T == Optional<Wrapped> {
self.init(value: node.ref.value.content) { onChange in
self.init(value: node.value.content) { onChange in
registry.addObserver(node: node, initial: true, onChange: { container in
onChange(container.content)
})
Expand Down
50 changes: 0 additions & 50 deletions Sources/CohesionKit/Observer/Observable.swift

This file was deleted.

12 changes: 6 additions & 6 deletions Sources/CohesionKit/Observer/ObserverRegistry.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,15 @@ class ObserverRegistry {
/// register an observer to observe changes on an entity node. Everytime `ObserverRegistry` is notified about changes
/// to this node `onChange` will be called.
func addObserver<T>(node: EntityNode<T>, key: ObjectKey, initial: Bool = false, onChange: @escaping (T) -> Void) -> Subscription {
let handler = Handler { onChange($0.ref.value) }
let handler = Handler { onChange($0.value) }

if initial {
if queue == DispatchQueue.main && Thread.isMainThread {
onChange(node.ref.value)
onChange(node.value)
}
else {
queue.sync {
onChange(node.ref.value)
onChange(node.value)
}
}
}
Expand All @@ -57,16 +57,16 @@ class ObserverRegistry {
func addObserver<T>(nodes: [EntityNode<T>], initial: Bool = false, onChange: @escaping ([T]) -> Void) -> Subscription {
let handler = Handler { (_: EntityNode<T>) in
// use last value from nodes
onChange(nodes.map(\.ref.value))
onChange(nodes.map(\.value))
}

if initial {
if queue == DispatchQueue.main && Thread.isMainThread {
onChange(nodes.map(\.ref.value))
onChange(nodes.map(\.value))
}
else {
queue.sync {
onChange(nodes.map(\.ref.value))
onChange(nodes.map(\.value))
}
}
}
Expand Down
21 changes: 21 additions & 0 deletions Sources/CohesionKit/Observer/Subscription.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import Foundation

public class Subscription {
public let unsubscribe: () -> Void

init(unsubscribe: @escaping () -> Void) {
var unsubscribed = false

self.unsubscribe = {
if !unsubscribed {
unsubscribe()
}

unsubscribed = true
}
}

deinit {
unsubscribe()
}
}
25 changes: 9 additions & 16 deletions Sources/CohesionKit/Storage/EntityNode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ struct EntityMetadata {
protocol AnyEntityNode: AnyObject {
associatedtype Value

var ref: Observable<Value> { get }
var value: Any { get }
var value: Value { get }
var metadata: EntityMetadata { get }
var storageKey: String { get }

Expand All @@ -47,34 +46,28 @@ class EntityNode<T>: AnyEntityNode {
let node: any AnyEntityNode
}

var value: Any { ref.value }
private(set) var value: Value

var metadata = EntityMetadata()
// FIXME: to delete, it's "just" to have a strong ref and avoid nodes to be deleted. Need a better memory management
private var childrenNodes: [any AnyEntityNode] = []

var applyChildrenChanges = true
/// An observable entity reference
let ref: Observable<T>

let storageKey: String

/// last time the ref.value was changed. Any subsequent change must have a higher value to be applied
/// last time `value` was changed. Any subsequent change must have a higher value to be applied
/// if nil ref has no stamp and any change will be accepted
private var modifiedAt: Stamp?
/// entity children
private(set) var children: [PartialKeyPath<T>: SubscribedChild] = [:]

init(ref: Observable<T>, key: String, modifiedAt: Stamp?) {
self.ref = ref
init(_ entity: T, key: String, modifiedAt: Stamp?) {
self.value = entity
self.modifiedAt = modifiedAt
self.storageKey = key
}

convenience init(_ entity: T, key: String, modifiedAt: Stamp?) {
self.init(ref: Observable(value: entity), key: key, modifiedAt: modifiedAt)
}

convenience init(_ entity: T, modifiedAt: Stamp?) where T: Identifiable {
let key = "\(T.self)-\(entity.id)"
self.init(entity, key: key, modifiedAt: modifiedAt)
Expand All @@ -89,11 +82,11 @@ class EntityNode<T>: AnyEntityNode {
}

modifiedAt = newModifiedAt ?? modifiedAt
ref.value = newEntity
value = newEntity
}

func nullify() -> Bool {
if let value = ref.value as? Nullable {
if let value = value as? Nullable {
do {
try updateEntity(value.nullified() as! T, modifiedAt: nil)
return true
Expand Down Expand Up @@ -126,12 +119,12 @@ class EntityNode<T>: AnyEntityNode {
}

if let writableKeyPath = keyPath as? WritableKeyPath<T, U.Value> {
ref.value[keyPath: writableKeyPath] = child.ref.value
value[keyPath: writableKeyPath] = child.value
return
}

if let optionalWritableKeyPath = keyPath as? WritableKeyPath<T, U.Value?> {
ref.value[keyPath: optionalWritableKeyPath] = child.ref.value
value[keyPath: optionalWritableKeyPath] = child.value
return
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class ObserverRegistryStub: ObserverRegistry {
}

func hasPendingChange<T: Equatable>(for entity: T) -> Bool {
pendingChangesStub.contains { ($0 as? EntityNode<T>)?.ref.value == entity }
pendingChangesStub.contains { ($0 as? EntityNode<T>)?.value == entity }
}

func hasPendingChange<T>(for _: T.Type) -> Bool {
Expand All @@ -22,7 +22,7 @@ class ObserverRegistryStub: ObserverRegistry {

/// number of times change has been inserted for this entity
func pendingChangeCount<T: Equatable>(for entity: T) -> Int {
pendingChangesStub.filter { ($0 as? EntityNode<T>)?.ref.value == entity }.count
pendingChangesStub.filter { ($0 as? EntityNode<T>)?.value == entity }.count
}

func clearPendingChangesStub() {
Expand Down
40 changes: 0 additions & 40 deletions Tests/CohesionKitTests/RefTests.swift

This file was deleted.

2 changes: 1 addition & 1 deletion Tests/CohesionKitTests/Storage/AliasStorageTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class AliasStorageTests: XCTestCase {
var storage: AliasStorage = [:]

XCTAssertNotNil(storage[safe: .testCollection])
XCTAssertNil(storage[safe: .testCollection].ref.value.content)
XCTAssertNil(storage[safe: .testCollection].value.content)
}

func test_subscriptGet_aliasHasSameNameThanAnotherType_itReturnsAliasContainer() {
Expand Down
10 changes: 5 additions & 5 deletions Tests/CohesionKitTests/Storage/EntityNodeTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,27 +30,27 @@ class EntityNodeTests: XCTestCase {
try node.updateEntity(newEntity, modifiedAt: startTimestamp)
)

XCTAssertEqual(node.value as? RootFixture, startEntity)
XCTAssertEqual(node.value, startEntity)
}

func test_updateEntity_stampIsSup_entityIsUpdated() throws {
try node.updateEntity(newEntity, modifiedAt: startTimestamp + 1)

XCTAssertEqual(node.value as? RootFixture, newEntity)
XCTAssertEqual(node.value, newEntity)
}

func test_updateEntity_stampIsInf_entityIsNotUpdated() throws {
XCTAssertThrowsError(
try node.updateEntity(newEntity, modifiedAt: startTimestamp - 1)
)

XCTAssertEqual(node.value as? RootFixture, startEntity)
XCTAssertEqual(node.value, startEntity)
}

func test_updateEntity_stampIsNil_entityIsUpdated() throws {
try node.updateEntity(newEntity, modifiedAt: nil)

XCTAssertEqual(node.value as? RootFixture, newEntity)
XCTAssertEqual(node.value, newEntity)
}

func test_updateEntity_stampIsNil_stampIsNotUpdated() throws {
Expand Down Expand Up @@ -94,7 +94,7 @@ class EntityNodeTests: XCTestCase {

node.updateEntityRelationship(childNode)

XCTAssertEqual(node.ref.value.singleNode, newChild)
XCTAssertEqual(node.value.singleNode, newChild)
}

func test_observeChild_childIsCollection_eachChildIsAdded() {
Expand Down

0 comments on commit 7e96304

Please sign in to comment.