Skip to content

Commit 844ffeb

Browse files
authored
Refactoring (#190)
* Simplify store state structure * Refactoring
1 parent 0b67111 commit 844ffeb

16 files changed

+293
-314
lines changed

Sources/Atoms/AtomStore.swift

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
/// An object that stores the state of atoms and its dependency graph.
22
@MainActor
33
public final class AtomStore {
4-
internal var graph = Graph()
5-
internal var state = StoreState()
4+
internal var dependencies = [AtomKey: Set<AtomKey>]()
5+
internal var children = [AtomKey: Set<AtomKey>]()
6+
internal var caches = [AtomKey: any AtomCacheProtocol]()
7+
internal var states = [AtomKey: any AtomStateProtocol]()
8+
internal var subscriptions = [AtomKey: [SubscriberKey: Subscription]]()
9+
internal var subscribes = [SubscriberKey: Set<AtomKey>]()
10+
internal var scopes = [ScopeKey: Scope]()
611

712
/// Creates a new store.
813
public nonisolated init() {}

Sources/Atoms/Core/Graph.swift

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

Sources/Atoms/Core/StoreContext.swift

Lines changed: 47 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,8 @@ internal struct StoreContext {
9898
let value = cache?.value ?? initialize(of: atom, for: key, override: override)
9999

100100
// Add an `Edge` from the upstream to downstream.
101-
store.graph.dependencies[transactionState.key, default: []].insert(key)
102-
store.graph.children[key, default: []].insert(transactionState.key)
101+
store.dependencies[transactionState.key, default: []].insert(key)
102+
store.children[key, default: []].insert(transactionState.key)
103103

104104
return value
105105
}
@@ -113,10 +113,10 @@ internal struct StoreContext {
113113
let (key, override) = lookupAtomKeyAndOverride(of: atom)
114114
let cache = lookupCache(of: atom, for: key)
115115
let value = cache?.value ?? initialize(of: atom, for: key, override: override)
116-
let isNewSubscription = store.state.subscribed[subscriber.key, default: []].insert(key).inserted
116+
let isNewSubscription = store.subscribes[subscriber.key, default: []].insert(key).inserted
117117

118118
if isNewSubscription {
119-
store.state.subscriptions[key, default: [:]][subscriber.key] = subscription
119+
store.subscriptions[key, default: [:]][subscriber.key] = subscription
120120
subscriber.unsubscribe = {
121121
unsubscribeAll(for: subscriber.key)
122122
}
@@ -224,22 +224,22 @@ internal struct StoreContext {
224224
func unwatch(_ atom: some Atom, subscriber: Subscriber) {
225225
let (key, _) = lookupAtomKeyAndOverride(of: atom)
226226

227-
store.state.subscribed[subscriber.key]?.remove(key)
227+
store.subscribes[subscriber.key]?.remove(key)
228228
unsubscribe([key], for: subscriber.key)
229229
}
230230

231231
@usableFromInline
232232
func registerScope(state: ScopeState) {
233233
let key = state.token.key
234234

235-
withUnsafeMutablePointer(to: &store.state.scopes[key]) { scope in
235+
withUnsafeMutablePointer(to: &store.scopes[key]) { scope in
236236
if scope.pointee == nil {
237237
scope.pointee = Scope()
238238
}
239239
}
240240

241241
state.unregister = {
242-
let scope = store.state.scopes.removeValue(forKey: key)
242+
let scope = store.scopes.removeValue(forKey: key)
243243

244244
if let scope {
245245
for key in scope.atoms {
@@ -252,9 +252,10 @@ internal struct StoreContext {
252252
@usableFromInline
253253
func snapshot() -> Snapshot {
254254
Snapshot(
255-
graph: store.graph,
256-
caches: store.state.caches,
257-
subscriptions: store.state.subscriptions
255+
dependencies: store.dependencies,
256+
children: store.children,
257+
caches: store.caches,
258+
subscriptions: store.subscriptions
258259
)
259260
}
260261

@@ -264,13 +265,13 @@ internal struct StoreContext {
264265
var disusedDependencies = [AtomKey: Set<AtomKey>]()
265266

266267
for key in keys {
267-
let oldDependencies = store.graph.dependencies[key]
268-
let newDependencies = snapshot.graph.dependencies[key]
268+
let oldDependencies = store.dependencies[key]
269+
let newDependencies = snapshot.dependencies[key]
269270

270271
// Update atom values and the graph.
271-
store.state.caches[key] = snapshot.caches[key]
272-
store.graph.dependencies[key] = newDependencies
273-
store.graph.children[key] = snapshot.graph.children[key]
272+
store.caches[key] = snapshot.caches[key]
273+
store.dependencies[key] = newDependencies
274+
store.children[key] = snapshot.children[key]
274275
disusedDependencies[key] = oldDependencies?.subtracting(newDependencies ?? [])
275276
}
276277

@@ -281,13 +282,13 @@ internal struct StoreContext {
281282
// Release dependencies that are no longer dependent.
282283
if let dependencies = disusedDependencies[key] {
283284
for dependency in dependencies {
284-
store.graph.children[dependency]?.remove(key)
285+
store.children[dependency]?.remove(key)
285286
checkAndRelease(for: dependency)
286287
}
287288
}
288289

289290
// Notify updates only for the subscriptions of restored atoms.
290-
if let subscriptions = store.state.subscriptions[key] {
291+
if let subscriptions = store.subscriptions[key] {
291292
for subscription in subscriptions.values {
292293
subscription.update()
293294
}
@@ -310,10 +311,10 @@ private extension StoreContext {
310311
state.effect.initializing(context: currentContext)
311312

312313
let value = getValue(of: atom, for: key, override: override)
313-
store.state.caches[key] = AtomCache(atom: atom, value: value, scopeValues: currentScopeValues)
314+
store.caches[key] = AtomCache(atom: atom, value: value, scopeValues: currentScopeValues)
314315

315316
if let scopeKey = key.scopeKey {
316-
store.state.scopes[scopeKey]?.atoms.insert(key)
317+
store.scopes[scopeKey]?.atoms.insert(key)
317318
}
318319

319320
state.effect.initialized(context: currentContext)
@@ -326,7 +327,7 @@ private extension StoreContext {
326327
cache: AtomCache<Node>,
327328
newValue: Node.Produced
328329
) {
329-
store.state.caches[key] = cache.updated(value: newValue)
330+
store.caches[key] = cache.updated(value: newValue)
330331

331332
// Check whether if the dependent atoms should be updated transitively.
332333
guard atom.producer.shouldUpdate(cache.value, newValue) else {
@@ -354,7 +355,7 @@ private extension StoreContext {
354355
// Overridden atoms don't get updated transitively.
355356
let newValue = localContext.getValue(of: cache.atom, for: key, override: nil)
356357

357-
store.state.caches[key] = cache.updated(value: newValue)
358+
store.caches[key] = cache.updated(value: newValue)
358359

359360
// Check whether if the dependent atoms should be updated transitively.
360361
guard cache.atom.producer.shouldUpdate(cache.value, newValue) else {
@@ -403,8 +404,8 @@ private extension StoreContext {
403404
continue
404405
}
405406

406-
let cache = store.state.caches[key]
407-
let dependencyCache = store.state.caches[edge.from]
407+
let cache = store.caches[key]
408+
let dependencyCache = store.caches[edge.from]
408409

409410
if let cache, let dependencyCache {
410411
performUpdate(dependency: dependencyCache.atom) {
@@ -417,8 +418,8 @@ private extension StoreContext {
417418
continue
418419
}
419420

420-
let subscription = store.state.subscriptions[edge.from]?[key]
421-
let dependencyCache = store.state.caches[edge.from]
421+
let subscription = store.subscriptions[edge.from]?[key]
422+
let dependencyCache = store.caches[edge.from]
422423

423424
if let subscription, let dependencyCache {
424425
performUpdate(dependency: dependencyCache.atom, body: subscription.update)
@@ -431,20 +432,20 @@ private extension StoreContext {
431432
}
432433

433434
func release(for key: AtomKey) {
434-
let dependencies = store.graph.dependencies.removeValue(forKey: key)
435-
let state = store.state.states.removeValue(forKey: key)
436-
let cache = store.state.caches.removeValue(forKey: key)
435+
let dependencies = store.dependencies.removeValue(forKey: key)
436+
let state = store.states.removeValue(forKey: key)
437+
let cache = store.caches.removeValue(forKey: key)
437438

438-
store.graph.children.removeValue(forKey: key)
439-
store.state.subscriptions.removeValue(forKey: key)
439+
store.children.removeValue(forKey: key)
440+
store.subscriptions.removeValue(forKey: key)
440441

441442
if let scopeKey = key.scopeKey {
442-
store.state.scopes[scopeKey]?.atoms.remove(key)
443+
store.scopes[scopeKey]?.atoms.remove(key)
443444
}
444445

445446
if let dependencies {
446447
for dependency in dependencies {
447-
store.graph.children[dependency]?.remove(key)
448+
store.children[dependency]?.remove(key)
448449
checkAndRelease(for: dependency)
449450
}
450451
}
@@ -465,7 +466,7 @@ private extension StoreContext {
465466
// 2. It has no downstream atoms.
466467
// 3. It has no subscriptions from views.
467468
lazy var shouldKeepAlive = {
468-
guard let cache = store.state.caches[key], cache.shouldKeepAlive else {
469+
guard let cache = store.caches[key], cache.shouldKeepAlive else {
469470
return false
470471
}
471472

@@ -474,10 +475,10 @@ private extension StoreContext {
474475
}
475476

476477
// It should keep alive untile the scope is unregistered.
477-
return store.state.scopes[scopeKey]?.atoms.contains(key) ?? false
478+
return store.scopes[scopeKey]?.atoms.contains(key) ?? false
478479
}()
479-
lazy var isChildrenEmpty = store.graph.children[key]?.isEmpty ?? true
480-
lazy var isSubscriptionEmpty = store.state.subscriptions[key]?.isEmpty ?? true
480+
lazy var isChildrenEmpty = store.children[key]?.isEmpty ?? true
481+
lazy var isSubscriptionEmpty = store.subscriptions[key]?.isEmpty ?? true
481482

482483
guard !shouldKeepAlive && isChildrenEmpty && isSubscriptionEmpty else {
483484
return
@@ -488,28 +489,28 @@ private extension StoreContext {
488489

489490
func detachDependencies(for key: AtomKey) -> Set<AtomKey> {
490491
// Remove current dependencies.
491-
let dependencies = store.graph.dependencies.removeValue(forKey: key) ?? []
492+
let dependencies = store.dependencies.removeValue(forKey: key) ?? []
492493

493494
// Detatch the atom from its children.
494495
for dependency in dependencies {
495-
store.graph.children[dependency]?.remove(key)
496+
store.children[dependency]?.remove(key)
496497
}
497498

498499
return dependencies
499500
}
500501

501502
func attachDependencies(_ dependencies: Set<AtomKey>, for key: AtomKey) {
502503
// Set dependencies.
503-
store.graph.dependencies[key] = dependencies
504+
store.dependencies[key] = dependencies
504505

505506
// Attach the atom to its children.
506507
for dependency in dependencies {
507-
store.graph.children[dependency]?.insert(key)
508+
store.children[dependency]?.insert(key)
508509
}
509510
}
510511

511512
func unsubscribeAll(for subscriberKey: SubscriberKey) {
512-
let keys = store.state.subscribed.removeValue(forKey: subscriberKey)
513+
let keys = store.subscribes.removeValue(forKey: subscriberKey)
513514

514515
if let keys {
515516
unsubscribe(keys, for: subscriberKey)
@@ -518,7 +519,7 @@ private extension StoreContext {
518519

519520
func unsubscribe(_ keys: some Sequence<AtomKey>, for subscriberKey: SubscriberKey) {
520521
for key in keys {
521-
store.state.subscriptions[key]?.removeValue(forKey: subscriberKey)
522+
store.subscriptions[key]?.removeValue(forKey: subscriberKey)
522523
checkAndRelease(for: key)
523524
}
524525

@@ -533,7 +534,7 @@ private extension StoreContext {
533534
let oldDependencies = detachDependencies(for: key)
534535

535536
return {
536-
let dependencies = store.graph.dependencies[key] ?? []
537+
let dependencies = store.dependencies[key] ?? []
537538
let disusedDependencies = oldDependencies.subtracting(dependencies)
538539

539540
// Release disused dependencies if no longer used.
@@ -584,12 +585,12 @@ private extension StoreContext {
584585
let currentContext = AtomCurrentContext(store: self)
585586
let effect = atom.effect(context: currentContext)
586587
let state = AtomState(effect: effect)
587-
store.state.states[key] = state
588+
store.states[key] = state
588589
return state
589590
}
590591

591592
func lookupState<Node: Atom>(of atom: Node, for key: AtomKey) -> AtomState<Node.Effect>? {
592-
guard let baseState = store.state.states[key] else {
593+
guard let baseState = store.states[key] else {
593594
return nil
594595
}
595596

@@ -616,7 +617,7 @@ private extension StoreContext {
616617
}
617618

618619
func lookupCache<Node: Atom>(of atom: Node, for key: AtomKey) -> AtomCache<Node>? {
619-
guard let baseCache = store.state.caches[key] else {
620+
guard let baseCache = store.caches[key] else {
620621
return nil
621622
}
622623

Sources/Atoms/Core/StoreState.swift

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

Sources/Atoms/Core/TopologicalSort.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@ internal extension AtomStore {
1919
var redundantDependencies = [Vertex: ContiguousArray<AtomKey>]()
2020

2121
func traverse(key: AtomKey, isRedundant: Bool) {
22-
if let children = graph.children[key] {
22+
if let children = children[key] {
2323
for child in children {
2424
traverse(key: child, from: key, isRedundant: isRedundant)
2525
}
2626
}
2727

28-
if let subscriptions = state.subscriptions[key] {
28+
if let subscriptions = subscriptions[key] {
2929
for subscriberKey in subscriptions.keys {
3030
traverse(key: subscriberKey, from: key, isRedundant: isRedundant)
3131
}

Sources/Atoms/Snapshot.swift

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
/// A snapshot structure that captures specific set of values of atoms and their dependency graph.
22
public struct Snapshot: CustomStringConvertible {
3-
internal let graph: Graph
3+
internal let dependencies: [AtomKey: Set<AtomKey>]
4+
internal let children: [AtomKey: Set<AtomKey>]
45
internal let caches: [AtomKey: any AtomCacheProtocol]
56
internal let subscriptions: [AtomKey: [SubscriberKey: Subscription]]
67

78
internal init(
8-
graph: Graph,
9+
dependencies: [AtomKey: Set<AtomKey>],
10+
children: [AtomKey: Set<AtomKey>],
911
caches: [AtomKey: any AtomCacheProtocol],
1012
subscriptions: [AtomKey: [SubscriberKey: Subscription]]
1113
) {
12-
self.graph = graph
14+
self.dependencies = dependencies
15+
self.children = children
1316
self.caches = caches
1417
self.subscriptions = subscriptions
1518
}
@@ -18,7 +21,8 @@ public struct Snapshot: CustomStringConvertible {
1821
public var description: String {
1922
"""
2023
Snapshot
21-
- graph: \(graph)
24+
- dependencies: \(dependencies)
25+
- children: \(children)
2226
- caches: \(caches)
2327
"""
2428
}
@@ -71,7 +75,7 @@ public struct Snapshot: CustomStringConvertible {
7175
for key in caches.keys {
7276
statements.insert(key.description.quoted)
7377

74-
if let children = graph.children[key] {
78+
if let children = children[key] {
7579
for child in children {
7680
statements.insert("\(key.description.quoted) -> \(child.description.quoted)")
7781
}

0 commit comments

Comments
 (0)