navidt
1
Hello! I have an idea that I have been pondering for a while. Swift KeyPaths have various appending methods, which allow us to chain them together. However, in my mind, the most natural way to do this is with a property access on the KeyPath which returns a new KeyPath with the property chained on, something like:
let path = \Double.description
let newPath = path.capitalized
This could be implemented by marking KeyPath with @dynamicMemberLookup and giving it the following subscript:
extension KeyPath {
subscript<T>(dynamicMember dynamicMember: KeyPath<Value, T>) -> KeyPath<Root, T> {
return appending(path: dynamicMember)
}
}
This approach complements the existing KeyPath literal syntax, as substituting path in the example above in the last line results in let newPath = \Double.description.capitalized, which is exactly what it is.
What do you think? This may also work with the Partial and Any KeyPaths, but I haven't given it much thought.
3 Likes
jrose
(Jordan Rose)
2
This is a cool bit of syntactic sugar, but I'd be worried about conflicts between members of the value type and members of the KeyPath type itself, like we had with Optional.none. I also feel like the times I've needed to append key paths, the path I was appending was usually a variable, not a known key path. Can you share an example where this would make your existing code cleaner?
1 Like
navidt
3
Thanks for the response! The main idea that got me thinking about this was the composition of KeyPath-based APIs. For example, let's say I am making a MediaList type, which uses SwiftUI's List. I want to be able to have some common media metadata that I can use. Following the lead of List, it can be initialized with some protocol that is not relevant to the discussion, or with a KeyPath for the metadata:
struct MediaMetadata {...}
struct MediaList<Data>: View {
var metadataPath: KeyPath<Data, MediaMetadata>
var mediaItems: [Data]
var body: some View { List(...) }
init(_ items: [Data], metadata: KeyPath<Data, MediaMetadata>) {...}
}
If the items are not identifiable, then I will have to write this for the list:
List(mediaItems, id: metadataPath.appending(path: \.mediaID) {...}
However, with this new way of creating chained KeyPaths, I can write this:
List(mediaItems, id: metadataPath.mediaID) {...}
It is easier to understand what is happening (for me at least), and this applies generally whenever you are layering KeyPath APIs.
1 Like
jrose
(Jordan Rose)
4
I see. I'm not sure I personally think that is clearer, but I couldn't picture the use case before and now I understand, so thank you!