Понижение частичного пути к ссылочному пути с использованием универсальных методов?

#swift #swift-keypath

Вопрос:

Мне нужна простая реализация сериализации, которая хранит значения различных ключевых путей для класса и применяет их. Я знаю, что это можно реализовать, сохранив переменные вручную, но я бы предпочел использовать ключевые пути, так как было бы проще добавить дополнительные пути. См. Пример ниже:

 class Model {
  var intValue: Int
  var boolValue: Bool
}

class ModelState {
  static let keyPaths = [ Model.intValue, Model.boolValue ]
  let values: [PartialKeyPath<Model>, Any]
  init(model: Model) {
    var values = [PartialKeyPath<Model>, Any]()
    Self.keyPaths.forEach { values[$0] = model[keyPath: $0 }
    self.values = values
  }
}

extension Model {
  var state: ModelState {
    get { return ModelState(model: self }
    set { newValue.values.forEach { (keyPath, value) in applyValue(keyPath, value) } }
  }

  private func applyValue<T>(_ keyPath: PartialKeyPath<Model>, value: T) {
    if let writeable = keyPath as? ReferenceWriteableKeyPath<Model, T> {
      self[keyPath: writeable] = value
    }
  } 
}
 

Если я добавлю точку останова в applyValue(), путь к ключу будет показан как ожидаемый путь к ссылкам. Аналогично, если я попытаюсь ввести ключевой путь, явно указав тип ожидаемого значения (например ReferenceWriteableKeyPath<Model, Int> , или ReferenceWriteableKeyPath<Model, Bool> ), типизация работает правильно. Однако написанный код никогда не входит в условие. Кто-нибудь видит проблему с кодом или знает, как заставить это общее уныние работать?

Комментарии:

1. Это очень сильно противоречит идее ключевых путей, вся суть которых заключалась в том, чтобы быть типобезопасными…

2. Я не вижу противоречия между тем, что я пытаюсь здесь сделать с целью обеспечения безопасности типов. Я хочу безопасно понизить ключевой путь до соответствующего типа значения, чтобы применить его, но я заинтересован в этом только для подмножества свойств модели. У вас есть какие-нибудь советы о том, как это может сработать?

3. Компилятор не может гарантировать типобезопасность вашего кода. В этом смысле он не является типобезопасным. В любом случае, непосредственная проблема заключается в том , что T это всегда Any так , поэтому вы пытаетесь привести к ReferenceWritableKeyPath<Model, Any> тому, что это не тот же тип, ReferenceWritableKeyPath<Model, Int> что и . Однако более фундаментальная проблема заключается в том, что вы используете что-то, что обеспечивает безопасность типов (доступные для записи пути ключей) небезопасным способом.

4. Хорошо, спасибо, это гораздо полезнее. Я отлаживал и печатал типы этих переменных с помощью type(of:), но вы правы в том, что универсальные типы являются функцией времени компиляции, поэтому при типизации всегда используется T == Any. У вас есть предложение о хорошем способе хранения и применения подмножества свойств объекта? Или в конечном итоге для этого потребуется просто создать структуру для хранения соответствующих свойств и присвоить структуре новое значение?