Получение строки из синтаксиса нового синтаксиса Swift 4?

Как вы можете получить строковое значение из синтаксиса Swift 4 smart keypaths (например, \Foo.bar)? В этот момент мне все равно интересно, неважно, осложнилось ли это.

Мне нравится идея информации о типе, связанной с интеллектуальным ключом. Но не все API и третьи стороны еще есть.

Там старый способ получения строки для имени свойства с проверкой времени компиляции с помощью #keyPath(). С Swift 4 для использования #keyPath() вы должны объявить свойство как @objc, чего я бы предпочел избежать.

Ответ 1

Для свойств Objective-C в классах Objective-C вы можете использовать свойство _kvcKeyPathString, чтобы получить его.

Однако в путях Swift могут быть не эквиваленты строк. Задача ключевых путей Swift заключается в том, что им не требуются имена полей, которые должны быть включены в исполняемый файл. Возможно, что путь ключа может быть представлен как последовательность смещений полей для получения или закрытия для вызова объекта.

Конечно, это напрямую противоречит вашей собственной цели - избегать объявления свойств @objc. Я считаю, что нет встроенного средства для выполнения того, что вы хотите сделать.

Ответ 2

Короткий ответ: вы не можете. Абстракция 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 */

Я бы не рекомендовал это решение для производственного кода, учитывая несколько высокий уровень обслуживания и т.д. Но поскольку вам было любопытно, что это, по крайней мере, дает вам путь вперед;)

Ответ 3

Немного поздно для вечеринки, но я наткнулся на способ получения строки ключевого пути из подклассов NSObject по крайней мере:

NSExpression(forKeyPath: \UIView.bounds).keyPath

Ответ 4

Это работает как расширение 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

Ответ 5

Расширяя ответ @Andy Heard, мы можем расширить KeyPath, чтобы получить вычисляемое свойство, например:

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

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