Question about property wrapper initialization

Considering that database storage is a good use case for property wrappers, I have made a small example that can read and write Codable values to a location in the Firebase Realtime Database using a property wrapper.

For those who don't know this database, it can basically be thought of as a key/value tree - similar to a JSON document. And thus every value in the database can be located through a path of keys.

For the following database content:

{
  "chatrooms": {
    "firechat": {
      "configuration": {
        "name": "Fire chat"
      }
    }
  }
}

The path /chatrooms/firechat/configuration could be considered to point out a Configuration entity that has a String based name property.

My property wrapper implementation is initialized with the path to the value in the database - and I have made this concept type safe by modelling a tree of a generic Path type that sort of describes a schema for the database.

Referencing the Configuration entity above can be done as follows:

@FirebaseValue(path: Path.chatrooms.child("firechat").configuration)
var configuration: Configuration = .default

This works very well when the path in question can be determined statically. If for instance I would like to create some view model that represented a specific chat room, it would be natural to let this view model be initialized with the key of that specific chat room.
E.g.:

class ViewModel {
  let chatroomKey: String

  @FirebaseValue(path: Path.chatrooms.child(self.chatroomKey).configuration)
  var configuration: Configuration = .default

  init(chatroomKey: String) {
    self.chatroomKey = chatroomKey
  }
}

This does not compile since I cannot refer to self.chatroomKey before initialization. Using another tecnique than property wrappers I might have made the property lazy in order to be able to refer to the key.

When it becomes possible to create local property wrappers, I can of course do something similar to:

class ViewModel {
  @FirebaseValue
  var configuration: Configuration = .default

  init(chatroomKey: String) {    
    @FirebaseValue(path: Path.chatrooms.child(chatroomKey).configuration)
    var cfg: Configuration = .default

    self._configuration = _cfg
  }
}

Or something similar. But still it doesn't read nearly as nicely as the version where the property in the viewmodel carried the path configuration directly.

So finally my questions:
Is my use case for property wrappers bad due to the fact that I would like a dynamic element of my property wrapper initialization?

Have other similar use cases been discussed during pitch or review? I haven't been able to find any.

Is there a chance that the feature might see some aspect of lazily initializing a whole property wrapper to enable something like what I am trying to do?

Or is there some clever trick I may use to achieve what I want?

1 Like

Using the Clamping example from the proposal, you can do something like this:

struct Q {
    let range = 1...7

    @Clamping var x: Int

    init(x: Int) {
        _x = Clamping(initialValue: x, range)
    }
}

i.e. you can initialize the the wrapper in init() just as you would any other variable. You don't need a local variable that is also wrapped.

1 Like

Thanks, right. But you lose a lot of the consiceness of the sugared initialiser.

Especially if you have multiple wrappers, the initialization becomes a bit bloated:

struct A {
    @ObjectBinding
    @FirebaseValue
    var configuration: Configuration

    init(chatroomKey: String) {
        _configuration = ObjectBinding(initialValue:
            FirebaseValue(initialValue: .default,
                          path:  Path.chatrooms.child(chatroomKey).configuration)
        )
    }
}