Ответ 1
ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ:
Обратите внимание, что следующий код предназначен только для образовательных целей и его не следует использовать в реальном приложении, и он может содержать множество ошибок/странного поведения, если используется KeyPath
таким образом.
Ответ:
Я не знаю, актуален ли ваш вопрос сегодня, но задача была веселой :)
На самом деле это возможно с помощью API зеркального отображения.
В настоящее время API-интерфейс KeyPath не позволяет инициализировать новый KeyPath из строки, но он поддерживает словарь "синтаксический анализ".
Идея состоит в том, чтобы создать словарь, который будет описывать struct
с использованием API зеркального отображения, а затем перебирать ключ для создания массива KeyPath.
Детская площадка Swift 4.2:
protocol KeyPathListable {
// require empty init as the implementation use the mirroring API, which require
// to be used on an instance. So we need to be able to create a new instance of the
// type.
init()
var _keyPathReadableFormat: [String: Any] { get }
static var allKeyPaths: [KeyPath<Foo, Any?>] { get }
}
extension KeyPathListable {
var _keyPathReadableFormat: [String: Any] {
let mirror = Mirror(reflecting: self)
var description: [String: Any] = [:]
for case let (label?, value) in mirror.children {
description[label] = value
}
return description
}
static var allKeyPaths: [KeyPath<Self, Any?>] {
var keyPaths: [KeyPath<Self, Any?>] = []
let instance = Self()
for (key, _) in instance._keyPathReadableFormat {
keyPaths.append(\Self._keyPathReadableFormat[key])
}
return keyPaths
}
}
struct Foo: KeyPathListable {
var x: Int
var y: Int
}
extension Foo {
// Custom init inside an extension to keep auto generated 'init(x:, y:)'
init() {
x = 0
y = 0
}
}
let xKey = Foo.allKeyPaths[0]
let yKey = Foo.allKeyPaths[1]
var foo = Foo(x: 10, y: 20)
let x = foo[keyPath: xKey]!
let y = foo[keyPath: yKey]!
print(x)
print(y)
Обратите внимание, что напечатанный вывод не всегда в одном и том же порядке (вероятно, из-за API зеркального отображения, но не уверен в этом).