问题
How can you get a string value from Swift 4 smart keypaths syntax (e.g., \Foo.bar
)? At this point I'm curious about any way at all, does not matter if it's complicated.
I like the idea of type information being associated with smart key path. But not all APIs and 3rd parties are there yet.
There's old way of getting string for property name with compile-time validation by #keyPath()
. With Swift 4 to use #keyPath()
you have to declare a property as @objc
, which is something I'd prefer to avoid.
回答1:
For Objective-C properties on Objective-C classes, you can use the _kvcKeyPathString property to get it.
However, Swift key paths may not have String equivalents. It is a stated objective of Swift key paths that they do not require field names to be included in the executable. It's possible that a key path could be represented as a sequence of offsets of fields to get, or closures to call on an object.
Of course, this directly conflicts with your own objective of avoiding to declare properties @objc
. I believe that there is no built-in facility to do what you want to do.
回答2:
Short answer: you can't. The KeyPath abstraction is designed to encapsulate a potentially nested property key path from a given root type. As such, exporting a single String
value might not make sense in the general case.
For instance, should the hypothetically exported string be interpreted as a property of the root type or a member of one of its properties? At the very least a string array would need to be exported to address such scenarios...
Per type workaround. Having said that, given that KeyPath
conforms to the Equatable
protocol, you can provide a custom, per type solution yourself. For instance:
struct Auth {
var email: String
var password: String
}
struct User {
var name: String
var auth: Auth
}
provide an extension for User
-based key paths:
extension PartialKeyPath where Root == User {
var stringValue: String {
switch self {
case \User.name: return "name"
case \User.auth: return "auth"
case \User.auth.email: return "auth.email"
case \User.auth.password: return "auth.password"
default: fatalError("Unexpected key path")
}
}
usage:
let name: KeyPath<User, String> = \User.name
let email: KeyPath<User, String> = \User.auth.email
print(name.stringValue) /* name */
print(email.stringValue) /* auth.email */
I wouldn't really recommend this solution for production code, given the somewhat high maintenance, etc. But since you were curious this, at least, gives you a way forward ;)
回答3:
A bit late to the party, but I've stumbled upon a way of getting a key path string from NSObject subclasses at least:
NSExpression(forKeyPath: \UIView.bounds).keyPath
回答4:
Expanding on @Andy Heard's answer we could extend KeyPath
to have a computed property, like this:
extension KeyPath where Root: NSObject {
var toString: String {
return NSExpression(forKeyPath: self).keyPath
}
}
// Usage
let stringValue = (\Foo.bar).toString
print(stringValue) // prints "bar"
回答5:
That works as a Swift 4 extension:
extension AnyKeyPath {
/// Returns key path represented as a string
var asString: String? {
return _kvcKeyPathString?.description
}
}
Usage:
(\UIView.bounds).asString
// or
let keyPath = \UIView.bounds
keyPath.asString
来源:https://stackoverflow.com/questions/46529015/getting-string-from-swift-4-new-key-path-syntax