Hello,
I want to have an object that observes and records what values get accessed from a stored variable and dynamically lookup the stored variables' values on access (that part works). But if no dynamic lookup (access to a child variable) is given, I want access to the ObservableState struct to return the value itself.
The goal here is to wrap the state as if it is the variable itself, and record the access to its child variables and to the variable itself. This recorded access is later used to determine whether a View-closure should be re-run. Access to the value itself (without dynamic lookup) is necessary because the state can for example be an Int or a struct that is copied and used elsewhere.
Is there a way?
Attempt 1: somehow returning the state itself when no variable is accessed? Found no way.
@dynamicMemberLookup
struct ObservableState<State> {
fileprivate let _state: CurrentValueRelay<State>
// ERROR: How do I do this?
var self: State {
// When no variable is accessed, return the value of _state.
_state.value
}
// Values of state.
public subscript<Value>(dynamicMember keyPath: KeyPath<State, Value>) -> Value {
return self._state.value[keyPath: keyPath]
}
}
Attempt 2: Property Wrapper does not work because accessing values goes directly over the wrapped value.
@propertyWrapper
@dynamicMemberLookup
public struct AccessObserved<State> {
let _state: CurrentValueRelay<State>
let comparisonState: ComparisonState
public var wrappedValue: State {
get {
print("Accessed base .state")
comparisonState.oneIsInequatable = true
return _state.value
}
}
// ERROR: Never called, because .dot-access just returns the wrapped values' children instead of going through these.
public subscript<Value>(dynamicMember keyPath: KeyPath<State, Value>) -> Value {
comparisonState.oneIsInequatable = true
return self._state.value[keyPath: keyPath]
}
public subscript<Value: Equatable>(dynamicMember keyPath: KeyPath<State, Value>) -> Value {
let value = self._state.value[keyPath: keyPath]
/// Store what values were accessed.
print("Accessed value \(String(describing: value)) \(comparisonState.id)")
comparisonState.accessedValues[keyPath] = AnyEquatable(value)
print("Accessed values, new count: \(comparisonState.accessedValues.count)")
return value
}
}
Context:
Currently, incase any variable in the state changes, the view-closure is re-evaluated. This can be scoped down, but the scoping can be tedious and people forget to scope properly sometimes. Using the detection of access allows checking only the accessed values for comparison, automatically ignoring changes to unused variables.