Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 13 additions & 8 deletions Sources/Atoms/AtomScope.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ import SwiftUI
///
public struct AtomScope<Content: View>: View {
private let inheritance: Inheritance
private var overrides = [OverrideKey: any AtomOverrideProtocol]()
private var observers = [Observer]()
private var overrides: [OverrideKey: any AtomOverrideProtocol]
private var observers: [Observer]
private let content: Content

/// Creates a new scope with the specified content.
Expand All @@ -61,6 +61,8 @@ public struct AtomScope<Content: View>: View {
public init<ID: Hashable>(id: ID = DefaultScopeID(), @ViewBuilder content: () -> Content) {
let id = ScopeID(id)
self.inheritance = .environment(id: id)
self.overrides = [:]
self.observers = []
self.content = content()
}

Expand All @@ -74,7 +76,10 @@ public struct AtomScope<Content: View>: View {
inheriting context: AtomViewContext,
@ViewBuilder content: () -> Content
) {
self.inheritance = .context(context)
let store = context._store
self.inheritance = .context(store: store)
self.overrides = store.scopedOverrides
self.observers = store.scopedObservers
self.content = content()
}

Expand All @@ -89,10 +94,10 @@ public struct AtomScope<Content: View>: View {
observers: observers
)

case .context(let context):
case .context(let store):
InheritedContext(
content: content,
context: context,
store: store,
overrides: overrides,
observers: observers
)
Expand Down Expand Up @@ -150,7 +155,7 @@ public struct AtomScope<Content: View>: View {
private extension AtomScope {
enum Inheritance {
case environment(id: ScopeID)
case context(AtomViewContext)
case context(store: StoreContext)
}

struct InheritedEnvironment: View {
Expand Down Expand Up @@ -184,14 +189,14 @@ private extension AtomScope {

struct InheritedContext: View {
let content: Content
let context: AtomViewContext
let store: StoreContext
let overrides: [OverrideKey: any AtomOverrideProtocol]
let observers: [Observer]

var body: some View {
content.environment(
\.store,
context._store.inherited(
store.inherited(
scopedObservers: observers,
scopedOverrides: overrides
)
Expand Down
9 changes: 5 additions & 4 deletions Sources/Atoms/Core/StoreContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ internal struct StoreContext {
private let scopeKey: ScopeKey
private let inheritedScopeKeys: [ScopeID: ScopeKey]
private let observers: [Observer]
private let scopedObservers: [Observer]
private let overrides: [OverrideKey: any AtomOverrideProtocol]
private let scopedOverrides: [OverrideKey: any AtomOverrideProtocol]

let scopedObservers: [Observer]
let scopedOverrides: [OverrideKey: any AtomOverrideProtocol]

init(
store: AtomStore,
Expand Down Expand Up @@ -38,9 +39,9 @@ internal struct StoreContext {
scopeKey: scopeKey,
inheritedScopeKeys: inheritedScopeKeys,
observers: observers,
scopedObservers: self.scopedObservers + scopedObservers,
scopedObservers: scopedObservers,
overrides: overrides,
scopedOverrides: self.scopedOverrides.merging(scopedOverrides) { $1 }
scopedOverrides: scopedOverrides
)
}

Expand Down
33 changes: 25 additions & 8 deletions Tests/AtomsTests/Core/StoreContextTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,34 +42,51 @@ final class StoreContextTests: XCTestCase {
let subscriber = Subscriber(subscriberState)
let scopeToken = ScopeKey.Token()
let scopeKey = ScopeKey(token: scopeToken)
let atom = TestAtom(value: 0)
let atom0 = TestAtom(value: 0)
let atom1 = TestAtom(value: 1)
var snapshots0 = [Snapshot]()
var snapshots1 = [Snapshot]()
var snapshots2 = [Snapshot]()
let context = StoreContext(
store: store,
scopeKey: scopeKey,
scopeKey: ScopeKey(token: ScopeKey.Token()),
observers: [
Observer { snapshots0.append($0) }
]
)
let inheritedContext = context.inherited(
scopedObservers: [
let scopedContext = context.scoped(
scopeKey: scopeKey,
scopeID: ScopeID(DefaultScopeID()),
observers: [
Observer { snapshots1.append($0) }
],
scopedOverrides: [
OverrideKey(atom): AtomOverride<TestAtom<Int>>(isScoped: true) { _ in
overrides: [
OverrideKey(atom0): AtomOverride<TestAtom<Int>>(isScoped: true) { _ in
10
}
]
)
let inheritedContext = scopedContext.inherited(
scopedObservers: scopedContext.scopedObservers + [
Observer { snapshots2.append($0) }
],
scopedOverrides: mutating(scopedContext.scopedOverrides) {
$0[OverrideKey(atom1)] = AtomOverride<TestAtom<Int>>(isScoped: true) { _ in
20
}
}
)

XCTAssertEqual(inheritedContext.watch(atom, subscriber: subscriber, requiresObjectUpdate: false) {}, 10)
XCTAssertEqual(inheritedContext.watch(atom0, subscriber: subscriber, requiresObjectUpdate: false) {}, 10)
XCTAssertEqual(inheritedContext.watch(atom1, subscriber: subscriber, requiresObjectUpdate: false) {}, 20)
XCTAssertFalse(snapshots0.isEmpty)
XCTAssertFalse(snapshots1.isEmpty)
XCTAssertFalse(snapshots2.isEmpty)
XCTAssertEqual(
store.state.caches.compactMapValues { $0 as? AtomCache<TestAtom<Int>> },
[
AtomKey(atom, scopeKey: scopeKey): AtomCache(atom: atom, value: 10)
AtomKey(atom0, scopeKey: scopeKey): AtomCache(atom: atom0, value: 10),
AtomKey(atom1, scopeKey: scopeKey): AtomCache(atom: atom1, value: 20),
]
)
}
Expand Down