从Swift 4获取字符串新的密钥路径语法?

时间:2017-10-02 15:46:59

标签: ios swift reflection swift4 key-value-coding

如何从Swift 4 smart keypaths语法(例如\Foo.bar)获取字符串值?在这一点上,我对任何方式都很好奇,如果它很复杂并不重要。

我喜欢将类型信息与智能密钥路径相关联的想法。但并非所有API和第三方都在那里。

#keyPath()通过编译时验证获取属性名字符串的旧方法。使用Swift 4来使用#keyPath(),你必须将属性声明为@objc,这是我宁愿避免的。

5 个答案:

答案 0 :(得分:21)

简短回答:你不能。 KeyPath抽象旨在封装来自给定根类型的潜在嵌套属性密钥路径。因此,在一般情况下,导出单个String值可能没有意义。

例如,假设导出的字符串是否应该被解释为根类型的属性或其属性之一的成员?至少需要导出一个字符串数组来解决这种情况......

每种类型的解决方法。话虽如此,鉴于KeyPath符合Equatable协议,您可以自己提供自定义的每种类型解决方案。例如:

struct Auth {
    var email: String
    var password: String
}
struct User {
    var name: String
    var auth: Auth
}

为基于User的关键路径提供扩展名

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")
    }
}

用法:

let name:  KeyPath<User, String> = \User.name
let email: KeyPath<User, String> = \User.auth.email
print(name.stringValue)  /* name */
print(email.stringValue) /* auth.email */

我不会真正推荐这个解决方案用于生产代码,因为维护有点高,等等。但是,因为你很好奇,至少可以为你提供前进的方法;)

答案 1 :(得分:16)

晚了一点,但我偶然发现了一种至少从NSObject子类中获取关键路径字符串的方法:

NSExpression(forKeyPath: \UIView.bounds).keyPath

答案 2 :(得分:15)

对于Objective-C类的Objective-C属性,您可以使用_kvcKeyPathString property来获取它。

但是,Swift键路径可能没有String等价物。它是Swift密钥路径的既定目标,它们不要求字段名称包含在可执行文件中。关键路径可能表示为要获取的字段偏移序列,或者可以表示要调用对象的闭包。

当然,这与您自己避免声明属性@objc的目标直接冲突。我相信没有内置工具可以做你想做的事。

答案 3 :(得分:1)

@Andy Heard's answer上进行扩展,我们可以扩展KeyPath以具有计算属性,如下所示:

extension KeyPath where Root: NSObject {
    var toString: String {
        return NSExpression(forKeyPath: self).keyPath
    }
}

// Usage
let stringValue = (\Foo.bar).toString
print(stringValue) // prints "bar"

答案 4 :(得分:0)

可以用作Swift 4扩展程序:

extension AnyKeyPath {
    /// Returns key path represented as a string
    var asString: String? {
        return _kvcKeyPathString?.description
    }
}

用法:

(\UIView.bounds).asString
// or
let keyPath = \UIView.bounds
keyPath.asString