Skip to content

Commit

Permalink
decision environment, cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
MaximBazarov committed Jan 6, 2024
1 parent e6cc476 commit 06f55f6
Show file tree
Hide file tree
Showing 16 changed files with 100 additions and 622 deletions.
5 changes: 2 additions & 3 deletions Decide-Tests/SwiftUI_Tests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import SwiftUI
import Decide
import XCTest
import DecideTesting

@MainActor final class SwiftUI_Tests: XCTestCase {

Expand All @@ -33,8 +32,8 @@ import DecideTesting
struct UpdateStr: ValueDecision {
var newValue: String

func mutate(_ env: Decide.DecisionEnvironment) {
// env[\.Storage.$str] = newValue
func mutate(_ env: DecisionEnvironment) {
var x = env[\Storage.$str]
}
}

Expand Down
13 changes: 3 additions & 10 deletions Decide/Binding/Bind.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,23 +47,16 @@ public final class Bind<Root, Value> where Root: StateRoot {
get {
let wrapperInstance = instance[keyPath: storageKeyPath]
let root = wrapperInstance.environment.get(Root.self)
let observableValue = root[keyPath: wrapperInstance.statePath]

#warning("""
TODO: Squash updates of any values this instance is subscribed to,
to one update to instance.
""")
let observer = Observer(wrapperInstance) { [weak instance] in
let observableValue = root[keyPath: wrapperInstance.statePath.appending(path: \.storage)]
let observer = Observer(instance) { [weak instance] in
instance?.onChange()
}
return observableValue.getValueSubscribing(observer: observer)
}
set {
let wrapperInstance = instance[keyPath: storageKeyPath]
let root = wrapperInstance.environment.get(Root.self)
let observableValue = root[keyPath: wrapperInstance.statePath]
let observableValue = root[keyPath: wrapperInstance.statePath.appending(path: \.storage)]
observableValue.set(value: newValue)
}
}
Expand Down
4 changes: 2 additions & 2 deletions Decide/Binding/SwiftUIBinding.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public struct SwiftUIBind<
public var wrappedValue: Value {
get {
environment
.get(Root.self)[keyPath: statePath]
.get(Root.self)[keyPath: statePath.appending(path: \.storage)]
.getValueSubscribing(
observer: Observer(publisher) { [weak publisher] in
publisher?.objectWillChange.send()
Expand All @@ -40,7 +40,7 @@ public struct SwiftUIBind<
}
nonmutating set {
environment
.get(Root.self)[keyPath: statePath]
.get(Root.self)[keyPath: statePath.appending(path: \.storage)]
.set(value: newValue)
}
}
Expand Down
78 changes: 58 additions & 20 deletions Decide/Decision/Decision.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,38 +51,76 @@ public typealias EnvironmentMutation = (DecisionEnvironment) -> Void
}

func make(decision: Decision) {
/**
1. Create a temporary environment, something to enable transactions
2. Perform that mutation
3. Execute effects
*/
decision.mutate(self)
observers.forEach { $0.send() }
performEffects()
observers = []
}

func perform(effect: Effect) {

func performEffects() {

}

private var observers: Set<Observer> = []

func getValue<Root: StateRoot, Value>(_ path: KeyPath<Root, ValueStorage<Value>>) -> Value {
let root = environment.get(Root.self)
let observableValue = root[keyPath: path]
return observableValue.value
}

func set<Root: StateRoot, Value>(value newValue: Value, path: KeyPath<Root, ValueStorage<Value>>) {
let root = environment.get(Root.self)
let observableValue = root[keyPath: path]
// We pop all observers of the observable values mutated by the decision.
// it also will prevent their observers to be notified
// until decision application is complete.
let valueObservers = observableValue.observation.popObservers()
// then we add them into set of all the observers that we will notify
// when all states affected by decision are mutated.
// this way we squash all the updates to:
// Each observer notified **once**,
// regardless how many values were updated.
// E.g. some object observes `A.a` `A.b` and `B.a` where A and B are roots.
// and .a .b are values.
// Decision updates all of them, but since it's a single observer (Observer.id),
// it will only get one update.
observers.formUnion(valueObservers)
observableValue.value = newValue
}

/**
Subscript to direct read/write access to any atomic state.
*/
public subscript<Root: StateRoot, Value>(_ path: KeyPath<Root, ObservableValue<Value>>) -> Value {
get {
let root = environment.get(Root.self)
let observableValue = root[keyPath: path]
return observableValue.wrappedValue
}
set {
let root = environment.get(Root.self)
let observableValue = root[keyPath: path]
observableValue.set(value: newValue)
}
public subscript<
Root: StateRoot,
Value
>(
_ path: KeyPath<Root, ObservableValue<Value>>
) -> Value {
get { getValue(path.appending(path: \.storage)) }
set { set(value: newValue, path: path.appending(path: \.storage)) }
}

public subscript<
Root: StateRoot,
Wrapper: ObservableValueStorageWrapper,
Value
>(
path: KeyPath<Root, Wrapper>
) -> Value where Wrapper.Value == Value {
get { getValue(path.appending(path: \.storage)) }
set { set(value: newValue, path: path.appending(path: \.storage)) }
}

/**
Subscript to direct read/write access to any identified state.
*/
public subscript<Identifier: Hashable, Root: IdentifiedStateRoot, Value>(
public subscript<
Identifier: Hashable,
Root: IdentifiedStateRoot,
Value
>(
_ path: KeyPath<Root, ObservableValue<Value>>,
at id: Identifier
) -> Value where Root.Identifier == Identifier {
Expand All @@ -93,7 +131,7 @@ public typealias EnvironmentMutation = (DecisionEnvironment) -> Void
}
set {
let root = environment.get(Root.self, at: id)
let observableValue = root[keyPath: path]
let observableValue = root[keyPath: path.appending(path: \.storage)]
observableValue.set(value: newValue)
}
}
Expand Down
9 changes: 7 additions & 2 deletions Decide/Environment/Observability.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,14 @@ import Foundation

/// Holds a week reference to actual observer, notifies only if object still exist.
public final class Observer: Hashable {
private(set) var notify: () -> Void
private var notify: () -> Void
private var id: ObjectIdentifier

public func send() {
print("notified \(id)")
notify()
}

@MainActor init<O: AnyObject>(_ observer: O, notify: @escaping () -> Void) {
self.notify = notify
self.id = ObjectIdentifier(observer)
Expand Down Expand Up @@ -51,7 +56,7 @@ public final class Observer: Hashable {

func sendAll() {
let observers = popObservers()
observers.forEach { $0.notify() }
observers.forEach { $0.send() }
}
}

15 changes: 0 additions & 15 deletions Decide/Macros.swift

This file was deleted.

5 changes: 3 additions & 2 deletions Decide/Persistency/Persistency.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,16 @@ import Foundation
*/
@propertyWrapper
@MainActor
public final class Persistent<Value>: ObservableValueWrapper {
public final class Persistent<Value>: ObservableValueStorageWrapper {
public var wrappedValue: Value { storage.value }
public var projectedValue: Persistent<Value> { self }
public var storage: ValueStorage<Value>

public init(wrappedValue: @autoclosure @escaping () -> Value) {
self.storage = ValueStorage(initialValue: wrappedValue)
}

public init<Wrapper: ObservableValueWrapper>(wrappedValue: Wrapper) where Wrapper.Value == Value {
public init<Wrapper: ObservableValueStorageWrapper>(wrappedValue: Wrapper) where Wrapper.Value == Value {
self.storage = wrappedValue.storage
}
}
5 changes: 3 additions & 2 deletions Decide/RemoteValue/RemoteSync.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,16 @@ import Foundation
*/
@propertyWrapper
@MainActor
public final class RemoteValue<Value>: ObservableValueWrapper {
public final class RemoteValue<Value>: ObservableValueStorageWrapper {
public var wrappedValue: Value { storage.value }
public var projectedValue: RemoteValue<Value> { self }
public var storage: ValueStorage<Value>

public init(wrappedValue: @autoclosure @escaping () -> Value) {
self.storage = ValueStorage(initialValue: wrappedValue)
}

public init<Wrapper: ObservableValueWrapper>(wrappedValue: Wrapper) where Wrapper.Value == Value {
public init<Wrapper: ObservableValueStorageWrapper>(wrappedValue: Wrapper) where Wrapper.Value == Value {
self.storage = wrappedValue.storage
}
}
30 changes: 8 additions & 22 deletions Decide/State/ObservableValue.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@
Contains the reference to the storage of the value.
*/
@MainActor
public protocol ObservableValueWrapper {
public protocol ObservableValueStorageWrapper {
associatedtype Value
var storage: ValueStorage<Value> { get }
var projectedValue: Self { get }
}

/**
Expand All @@ -28,35 +29,20 @@ public protocol ObservableValueWrapper {
*/
@propertyWrapper
@MainActor
public final class ObservableValue<Value> {

public final class ObservableValue<Value>: ObservableValueStorageWrapper {
public var wrappedValue: Value {
valueStorage.value
storage.value
}

public var projectedValue: ObservableValue<Value> { self }

var valueStorage: ValueStorage<Value>
var observation = ObserverStorage()
public var storage: ValueStorage<Value>

public init(wrappedValue: @autoclosure @escaping () -> Value) {
self.valueStorage = ValueStorage(initialValue: wrappedValue)
self.storage = ValueStorage(initialValue: wrappedValue)
}

public init<Wrapper: ObservableValueWrapper>(wrappedValue: Wrapper) where Wrapper.Value == Value {
self.valueStorage = wrappedValue.storage
public init<Wrapper: ObservableValueStorageWrapper>(wrappedValue: Wrapper) where Wrapper.Value == Value {
self.storage = wrappedValue.storage
}
}

extension ObservableValue {
public func getValueSubscribing(observer: Observer) -> Value {
observation.subscribe(observer)
return wrappedValue
}

public func set(value newValue: Value) {
valueStorage.value = newValue
observation.sendAll()
}
}

13 changes: 13 additions & 0 deletions Decide/State/ValueStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import Foundation

@MainActor
public final class ValueStorage<Value> {
var observation = ObserverStorage()
public var initialValue: () -> Value
public var value: Value {
get {
Expand All @@ -39,3 +40,15 @@ public final class ValueStorage<Value> {
self.initialValue = initialValue
}
}

extension ValueStorage {
public func getValueSubscribing(observer: Observer) -> Value {
observation.subscribe(observer)
return value
}

public func set(value newValue: Value) {
value = newValue
observation.sendAll()
}
}
14 changes: 0 additions & 14 deletions DecideMacros-Tests/Decide_Tests.swift

This file was deleted.

53 changes: 0 additions & 53 deletions DecideMacros/EnvironmentObservable.swift

This file was deleted.

Loading

0 comments on commit 06f55f6

Please sign in to comment.