@dynamicMemberLookup
struct DriverFor<Base> {
var base: Base
init(base: Base) {
self.base = base
}
subscript<T>(dynamicMember keyPath: KeyPath<Base, T>) -> T {
return base[keyPath: keyPath]
}
subscript<T>(dynamicMember keyPath: WritableKeyPath<Base, T>) -> T {
get { return base[keyPath: keyPath] }
set { base[keyPath: keyPath] = newValue }
}
}
protocol DriverConfigurator {
associatedtype Drivable
typealias Driver = DriverFor<Drivable>
typealias DrivableKeyPathSet = PartialKeyPathSet<Drivable>
///
func configure(_ driver: inout Driver, using set: DrivableKeyPathSet)
}
// FIXME: This protocol would not allow sub-classes to extend the context.
protocol Updatable {
associatedtype UpdateContext
func update(using context: UpdateContext)
}
Drivable
can be configured through the Driver
. The user can decide how Drivable
should be configured, either partly . DrivableKeyPathSet
contains key-paths the called wishes to configure.
- If the set is empty then it is recommend to do nothing.
- If the set contains the identity key-path the user should usually fully configure the
Drivable
. - For any other key-path the user can conditionally configure the
Drivable
partially.
struct ConcreteConfigurator: DriverConfigurator {
typealias Drivable = ConcreteDrivableType
func configure(_ driver: inout Driver, using set: DrivableKeyPathSet) {
if set.containsIdentity(or: \.first) {
driver.base.first = /* we could obtain the value from some cache */
}
if set.containsIdentity(or: \.second.value) {
// `.base` omitted due to `key-path member lookup`
driver.second.value = /* we could compute the value */
}
}
}
extension Collection where Element: Equatable {
/// Checks if current collection contains at least one
/// element from `other` collection.
func containsAny<C>(
from other: C
) -> Bool where C: Collection, C.Element == Element {
return other.contains(where: self.contains)
}
}
/// A set of key-paths with the same `Root` type.
typealias PartialKeyPathSet<Root> = Set<PartialKeyPath<Root>>
// FIXME: Convert the extension to:
// `extension<Root> where Element: PartialKeyPath<Root>`
extension Collection where Element: AnyKeyPath {
func containsIdentity<Root>(
or keyPath: PartialKeyPath<Root>
) -> Bool where Element == PartialKeyPath<Root> {
return containsAny(from: [\.self, keyPath])
}
}