Thoughts about propagation updates of variables to storage from bottom to top

Some thoughts about dynamic member lookup and nested structures.

Suppose that you have two different types that represent settings and data.

class Model {
  struct Settings {
    var setting1: Bool = false
    var setting2: Bool = false
  }

  struct Data {
    var value1: String = ""
    var value2: String = ""
  }

  var settings: Settings
  var data: Data
}

We can obviously add dynamic member lookup to access properties and, of course, perform actions after set values of these properties.

// MARK: Member Lookup
@dynamicMemberLookup
extension Model {
  public subscript<T>(dynamicMember keyPath: WritableKeyPath<Settings, T>) -> T {
      get { self.settings[keyPath: keyPath] }
      set {
          self.settings[keyPath: keyPath] = newValue
          self.didUpdateValues()
      }
  }
  public subscript<T>(dynamicMember keyPath: WritableKeyPath<Data, T>) -> T {
      get { self.data[keyPath: keyPath] }
      set {
          self.data[keyPath: keyPath] = newValue
          self.didUpdateValues()
      }
  }
}

// MARK: Calculations
extension Model {
  func didUpdateValues() {
    // perform any necessary computations on new values.
    // utilize both data and settings values.
  }
}

This scenario is related to decomposition of complex structures ( Hi, SwiftUI ). So far, so good. In ideal, we need a protocol or a generic type which utilizes protocol which exposes properties of structure.

// generated automatically when structure is in dynamicMemberLookup relationship.
// also it can contain any information about properties access ( write or readonly ).
protocol Model.Settings.PropertiesOnlyProtocol {
  var setting1: Bool {get set}
  var setting2: Bool {get set}
}

After that we can substitue structure by a dynamic-member-lookupable-type which adopts properties only protocol.

struct Usage {
  var propertiesOfSettings: Model.Settings.PropertiesOnlyProtocol = Model()
}

// MARK: Settings
// generated automatically.
extension Settings: Model.Settings.PropertiesOnlyProtocol {}

// MARK: Model
// generated automatically in case of member lookup.
// it also can specify type of keypath. ( PropertiesOnlyProtocol.Writeable )
extension Model: Model.Settings.PropertiesOnlyProtocol {}