subscript<T>(keyPath keyPath: WritableKeyPath<Self, T>) -> T { ... }
it won't be called when I'm getting/setting fields directly.
Could we have another subscript in addition, say:
subscript<T>(proxy keyPath: WritableKeyPath<Self, T>) -> T { ... }
which if specified would be called when I'm getting / setting fields directly?
struct S {
var x = 1, y = 2
subscript<T>(proxy keyPath: WritableKeyPath<Self, T>) -> T {
get {
// call though default implementation:
let result = self[keyPath: keyPath]
print("get \(keyPath) -> \(result)")
return result
}
set {
print("set \(keyPath) = \(newValue)")
// call though default implementation:
self[keyPath: keyPath] = newValue
}
}
}
var s = S()
s.x = 42 // set \S.x = 42
print(s.x) // get \S.x -> 42
This new "proxy" (bikeshed name) subscript will fill the gap between "keyPath" subscript and "dynamicMember" subscript.
I guess I could use this approach instead:
class S {
var x: Int {
get {
print("get \(\S.x) -> \(_x)")
return _x
}
set {
_x = newValue
print("set \(\S.x) = \(newValue)")
}
}
var _x: Int = 1
var y: Int {
get {
print("get \(\S.y) -> \(_y)")
return _y
}
set {
_y = newValue
print("set \(\S.y) = \(newValue)")
}
}
var _y: Int = 2
// and so on and so forth
}
or the equivalent code generated by macro, similar to how new Observation machinery is doing it.
I wonder though if the keypath subscript based approach is superior; IMHO it feels lighter and more elegant.
@dynamicMemberLookup public struct S {
public struct Storage {
var x: Int
var y: Int
}
private var storage: Storage
public subscript<T>(dynamicMember member: WritableKeyPath<Storage, T>) {
get { storage[keyPath: member] }
set { storage[keyPath: member] = newValue }
}
}