Appending two keypaths in Swift
First of all to use Swift 4 keypaths the objects are not required to inherit from NSObject
(not even to be a class) and have KVC compliant properties.
Second of all NSObject
is similar to AnyObject
but Swift 4 key paths need concrete static types.
Finally the property source
is optional so you have to unwrap the key path to be valid.
To be able to concatenate key paths the last component of the first key path must be of the same type as the first component of the second key path
This is the .appending Rule from WWDC 2017 - Session 212: What's new in Foundation
You can append key paths this way
class Foo {
var items = [1, 2, 3]
}
class Bar {
var source: Foo?
}
let bar = Bar()
bar.source = Foo()
let keyPath1 = \Bar.source!
let keyPath2 = \Foo.items
let keyPath = keyPath1.appending(path: keyPath2)
let items = bar[keyPath:keyPath]
print(items)
In Swift 4, how can you assign to a keypath when the type of the keypath and value are generic but the same?
This implementation is similar to what you provided as an example of an approach you tried, and I believe it produces the result you are looking for:
struct WritableKeyPathApplicator<Type> {
private let applicator: (Type, Any) -> Type
init<ValueType>(_ keyPath: WritableKeyPath<Type, ValueType>) {
applicator = {
var instance = $0
if let value = $1 as? ValueType {
instance[keyPath: keyPath] = value
}
return instance
}
}
func apply(value: Any, to: Type) -> Type {
return applicator(to, value)
}
}
struct Foo {
var bar: String = ""
var baz: Int? = nil
}
let values: [Any] = ["foo", 1337]
let fooPaths: [WritableKeyPathApplicator<Foo>] = [WritableKeyPathApplicator(\Foo.bar), WritableKeyPathApplicator(\Foo.baz)]
let protoFoo = zip(fooPaths, values).reduce(Foo()){ return $1.0.apply(value: $1.1, to: $0) }
print(protoFoo) // prints Foo(bar: "foo", baz: Optional(1337))
A version of this that does the same thing, but using a for loop as specified in the original question could replace the second-to-last line with:
var protoFoo = Foo()
for (applicator, value) in zip(fooPaths, values) {
protoFoo = applicator.apply(value: value, to: protoFoo)
}
Note that some type erasure is needed to work with a single array of keyPaths that operate on the same root type but have different value types. However, at runtime, the closure inside WritableKeyPathApplicator
can specifically cast to the correct original value type for each keyPath and either silently skip the value to set if it's the wrong type (as in the example code above), or it could throw an error or print more helpful information to the console, etc.
Related Topics
How to Add a Show More/Show Less Uibutton to Control Uitextview
How to Move Application's Window Between Virtual Desktops in Os X
Swift-Setting a Physics Body Velocity by Angle
How to Add 3D Shapes in Swift Ui
How to Create a Hotspot Network in iOS App Using Swift
How to Synchronize Coredata with Webservices in Swift
Format Println Output in a Table
Switching a @State Property to a @Binding Property Interferes with Animation
How to Replace the Values of Labels in iOS-Charts
Swift Sprite Kit in App Purchase
Property with '= {Return}()' or '{Return}'
Swift Firebase Using an Unspecified Index
How to Perform Face Detection in Swift
Textfield Delegate Shouldchangecharactersinrange
Custom Annotation Showing Same Image for All Different Types of Poi'S