Skip to content

Commit 16d69a3

Browse files
authored
Internal refactoring (#127)
* Implement topological order update algorithm * Refactoring * Refactoring * Track skipping atoms accurately * Add test cases * Refactoring * Support for complex cases of when transitive update is skipped * Refactoring * Add more strict testing * Show all diffs when validation fails * Fix dev tool cache * Refactoring * Update test case to be more strict * Refactoring * Add test for topological sort function * Optimize various use of collection * Refactor prepareForTransaction * Minor refactoring * Minor refactoring * Refactor watch * Refactor Transaction * Minor refactoring * Minor refactoring
1 parent 1bc21c6 commit 16d69a3

29 files changed

+295
-265
lines changed

Sources/Atoms/AtomRoot.swift

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ import SwiftUI
6262
///
6363
public struct AtomRoot<Content: View>: View {
6464
private var storeHost: StoreHost
65-
private var overrides = [OverrideKey: any AtomOverrideProtocol]()
65+
private var overrides = [OverrideKey: any OverrideProtocol]()
6666
private var observers = [Observer]()
6767
private let content: Content
6868

@@ -92,14 +92,14 @@ public struct AtomRoot<Content: View>: View {
9292
public var body: some View {
9393
switch storeHost {
9494
case .tree:
95-
TreeHostedStore(
95+
TreeManaged(
9696
content: content,
9797
overrides: overrides,
9898
observers: observers
9999
)
100100

101101
case .unmanaged(let store):
102-
UnmanagedStore(
102+
Unmanaged(
103103
content: content,
104104
store: store,
105105
overrides: overrides,
@@ -129,7 +129,7 @@ public struct AtomRoot<Content: View>: View {
129129
///
130130
/// - Returns: The self instance.
131131
public func override<Node: Atom>(_ atom: Node, with value: @escaping (Node) -> Node.Loader.Value) -> Self {
132-
mutating(self) { $0.overrides[OverrideKey(atom)] = AtomOverride(isScoped: false, value: value) }
132+
mutating(self) { $0.overrides[OverrideKey(atom)] = Override(isScoped: false, value: value) }
133133
}
134134

135135
/// Overrides the atoms with the given value.
@@ -145,7 +145,7 @@ public struct AtomRoot<Content: View>: View {
145145
///
146146
/// - Returns: The self instance.
147147
public func override<Node: Atom>(_ atomType: Node.Type, with value: @escaping (Node) -> Node.Loader.Value) -> Self {
148-
mutating(self) { $0.overrides[OverrideKey(atomType)] = AtomOverride(isScoped: false, value: value) }
148+
mutating(self) { $0.overrides[OverrideKey(atomType)] = Override(isScoped: false, value: value) }
149149
}
150150
}
151151

@@ -155,15 +155,15 @@ private extension AtomRoot {
155155
case unmanaged(store: AtomStore)
156156
}
157157

158-
struct TreeHostedStore: View {
158+
struct TreeManaged: View {
159159
@MainActor
160160
final class State: ObservableObject {
161161
let store = AtomStore()
162162
let token = ScopeKey.Token()
163163
}
164164

165165
let content: Content
166-
let overrides: [OverrideKey: any AtomOverrideProtocol]
166+
let overrides: [OverrideKey: any OverrideProtocol]
167167
let observers: [Observer]
168168

169169
@StateObject
@@ -185,15 +185,15 @@ private extension AtomRoot {
185185
}
186186
}
187187

188-
struct UnmanagedStore: View {
188+
struct Unmanaged: View {
189189
@MainActor
190190
final class State: ObservableObject {
191191
let token = ScopeKey.Token()
192192
}
193193

194194
let content: Content
195195
let store: AtomStore
196-
let overrides: [OverrideKey: any AtomOverrideProtocol]
196+
let overrides: [OverrideKey: any OverrideProtocol]
197197
let observers: [Observer]
198198

199199
@StateObject

Sources/Atoms/AtomScope.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ import SwiftUI
4949
///
5050
public struct AtomScope<Content: View>: View {
5151
private let inheritance: Inheritance
52-
private var overrides: [OverrideKey: any AtomOverrideProtocol]
52+
private var overrides: [OverrideKey: any OverrideProtocol]
5353
private var observers: [Observer]
5454
private let content: Content
5555

@@ -130,7 +130,7 @@ public struct AtomScope<Content: View>: View {
130130
///
131131
/// - Returns: The self instance.
132132
public func scopedOverride<Node: Atom>(_ atom: Node, with value: @escaping (Node) -> Node.Loader.Value) -> Self {
133-
mutating(self) { $0.overrides[OverrideKey(atom)] = AtomOverride(isScoped: true, value: value) }
133+
mutating(self) { $0.overrides[OverrideKey(atom)] = Override(isScoped: true, value: value) }
134134
}
135135

136136
/// Override the atoms used in this scope with the given value.
@@ -148,7 +148,7 @@ public struct AtomScope<Content: View>: View {
148148
///
149149
/// - Returns: The self instance.
150150
public func scopedOverride<Node: Atom>(_ atomType: Node.Type, with value: @escaping (Node) -> Node.Loader.Value) -> Self {
151-
mutating(self) { $0.overrides[OverrideKey(atomType)] = AtomOverride(isScoped: true, value: value) }
151+
mutating(self) { $0.overrides[OverrideKey(atomType)] = Override(isScoped: true, value: value) }
152152
}
153153
}
154154

@@ -166,7 +166,7 @@ private extension AtomScope {
166166

167167
let id: ScopeID
168168
let content: Content
169-
let overrides: [OverrideKey: any AtomOverrideProtocol]
169+
let overrides: [OverrideKey: any OverrideProtocol]
170170
let observers: [Observer]
171171

172172
@StateObject
@@ -190,7 +190,7 @@ private extension AtomScope {
190190
struct InheritedContext: View {
191191
let content: Content
192192
let store: StoreContext
193-
let overrides: [OverrideKey: any AtomOverrideProtocol]
193+
let overrides: [OverrideKey: any OverrideProtocol]
194194
let observers: [Observer]
195195

196196
var body: some View {
Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,27 @@
11
/// A context structure for notifying modifier value updates.
22
@MainActor
33
public struct AtomModifierContext<Value> {
4-
internal let _transaction: Transaction
5-
internal let _update: @MainActor (Value) -> Void
4+
private let transaction: Transaction
5+
private let update: @MainActor (Value) -> Void
66

77
internal init(
88
transaction: Transaction,
99
update: @escaping @MainActor (Value) -> Void
1010
) {
11-
_transaction = transaction
12-
_update = update
11+
self.transaction = transaction
12+
self.update = update
13+
}
14+
15+
/// A callback to invoke when an atom is released or updated to a new value.
16+
public var onTermination: (@MainActor () -> Void)? {
17+
get { transaction.onTermination }
18+
nonmutating set { transaction.onTermination = newValue }
1319
}
1420

1521
/// Notifies value updates.
1622
///
1723
/// - Parameter value: An updated value.
1824
public func update(with value: Value) {
19-
_update(value)
20-
}
21-
22-
/// Adds a termination action to be performed when the atom value is updated or released.
23-
///
24-
/// - Parameter termination: A termination action.
25-
public func addTermination(_ termination: @MainActor @escaping () -> Void) {
26-
_transaction.addTermination(termination)
25+
update(value)
2726
}
2827
}

Sources/Atoms/Context/AtomTestContext.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ public struct AtomTestContext: AtomWatchableContext {
367367
/// - value: A value to be used instead of the atom's value.
368368
@inlinable
369369
public func override<Node: Atom>(_ atom: Node, with value: @escaping (Node) -> Node.Loader.Value) {
370-
_state.overrides[OverrideKey(atom)] = AtomOverride(isScoped: false, value: value)
370+
_state.overrides[OverrideKey(atom)] = Override(isScoped: false, value: value)
371371
}
372372

373373
/// Overrides the atom value with the given value.
@@ -382,7 +382,7 @@ public struct AtomTestContext: AtomWatchableContext {
382382
/// - value: A value to be used instead of the atom's value.
383383
@inlinable
384384
public func override<Node: Atom>(_ atomType: Node.Type, with value: @escaping (Node) -> Node.Loader.Value) {
385-
_state.overrides[OverrideKey(atomType)] = AtomOverride(isScoped: false, value: value)
385+
_state.overrides[OverrideKey(atomType)] = Override(isScoped: false, value: value)
386386
}
387387
}
388388

@@ -396,7 +396,7 @@ internal extension AtomTestContext {
396396
let subscriberState = SubscriberState()
397397

398398
@usableFromInline
399-
var overrides = [OverrideKey: any AtomOverrideProtocol]()
399+
var overrides = [OverrideKey: any OverrideProtocol]()
400400

401401
@usableFromInline
402402
var onUpdate: (() -> Void)?

Sources/Atoms/Core/Loader/AsyncSequenceAtomLoader.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public struct AsyncSequenceAtomLoader<Node: AsyncSequenceAtom>: RefreshableAtomL
3030
}
3131
}
3232

33-
context.addTermination(task.cancel)
33+
context.onTermination = task.cancel
3434

3535
return .suspending
3636
}
@@ -61,7 +61,7 @@ public struct AsyncSequenceAtomLoader<Node: AsyncSequenceAtom>: RefreshableAtomL
6161
return phase
6262
}
6363

64-
context.addTermination(task.cancel)
64+
context.onTermination = task.cancel
6565

6666
return await withTaskCancellationHandler {
6767
await task.value

Sources/Atoms/Core/Loader/AtomLoaderContext.swift

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,26 +23,27 @@ public struct AtomLoaderContext<Value, Coordinator> {
2323
}
2424

2525
internal var modifierContext: AtomModifierContext<Value> {
26-
AtomModifierContext(transaction: transaction) { value in
27-
update(with: value)
28-
}
26+
AtomModifierContext(transaction: transaction, update: update)
2927
}
3028

31-
internal func update(with value: Value) {
32-
update(value)
29+
internal var onTermination: (@MainActor () -> Void)? {
30+
get { transaction.onTermination }
31+
nonmutating set { transaction.onTermination = newValue }
3332
}
3433

35-
internal func addTermination(_ termination: @MainActor @escaping () -> Void) {
36-
transaction.addTermination(termination)
34+
internal func update(with value: Value) {
35+
update(value)
3736
}
3837

3938
internal func transaction<T>(_ body: @MainActor (AtomTransactionContext<Coordinator>) -> T) -> T {
39+
transaction.begin()
4040
let context = AtomTransactionContext(store: store, transaction: transaction, coordinator: coordinator)
4141
defer { transaction.commit() }
4242
return body(context)
4343
}
4444

4545
internal func transaction<T>(_ body: @MainActor (AtomTransactionContext<Coordinator>) async throws -> T) async rethrows -> T {
46+
transaction.begin()
4647
let context = AtomTransactionContext(store: store, transaction: transaction, coordinator: coordinator)
4748
defer { transaction.commit() }
4849
return try await body(context)

Sources/Atoms/Core/Loader/ObservableObjectAtomLoader.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public struct ObservableObjectAtomLoader<Node: ObservableObjectAtom>: AtomLoader
3535
}
3636
}
3737

38-
context.addTermination(cancellable.cancel)
38+
context.onTermination = cancellable.cancel
3939

4040
return value
4141
}

Sources/Atoms/Core/Loader/PublisherAtomLoader.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public struct PublisherAtomLoader<Node: PublisherAtom>: RefreshableAtomLoader {
2525
}
2626
}
2727

28-
context.addTermination(task.cancel)
28+
context.onTermination = task.cancel
2929

3030
return .suspending
3131
}
@@ -50,7 +50,7 @@ public struct PublisherAtomLoader<Node: PublisherAtom>: RefreshableAtomLoader {
5050
return phase
5151
}
5252

53-
context.addTermination(task.cancel)
53+
context.onTermination = task.cancel
5454

5555
return await withTaskCancellationHandler {
5656
await task.value

Sources/Atoms/Core/Loader/TaskAtomLoader.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public struct TaskAtomLoader<Node: TaskAtom>: AsyncAtomLoader {
2828

2929
/// Manage given overridden value updates and cancellations.
3030
public func manageOverridden(value: Value, context: Context) -> Value {
31-
context.addTermination(value.cancel)
31+
context.onTermination = value.cancel
3232
return value
3333
}
3434

@@ -43,7 +43,7 @@ public struct TaskAtomLoader<Node: TaskAtom>: AsyncAtomLoader {
4343
/// Refreshes and waits for the passed value to finish outputting values
4444
/// and returns a final value.
4545
public func refresh(overridden value: Value, context: Context) async -> Value {
46-
context.addTermination(value.cancel)
46+
context.onTermination = value.cancel
4747

4848
return await withTaskCancellationHandler {
4949
_ = await value.result

Sources/Atoms/Core/Loader/ThrowingTaskAtomLoader.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public struct ThrowingTaskAtomLoader<Node: ThrowingTaskAtom>: AsyncAtomLoader {
2828

2929
/// Manage given overridden value updates and cancellations.
3030
public func manageOverridden(value: Value, context: Context) -> Value {
31-
context.addTermination(value.cancel)
31+
context.onTermination = value.cancel
3232
return value
3333
}
3434

@@ -43,7 +43,7 @@ public struct ThrowingTaskAtomLoader<Node: ThrowingTaskAtom>: AsyncAtomLoader {
4343
/// Refreshes and waits for the passed value to finish outputting values
4444
/// and returns a final value.
4545
public func refresh(overridden value: Value, context: Context) async -> Value {
46-
context.addTermination(value.cancel)
46+
context.onTermination = value.cancel
4747

4848
return await withTaskCancellationHandler {
4949
_ = await value.result

0 commit comments

Comments
 (0)