Problem with Property Wrapper composition: `@AppStorage("x") @Wrap var x: Int = 5`: `$x` is `Binding<Wrap>`, how to get `Binding<Int>`?

import SwiftUI

@propertyWrapper
struct Wrap {
    var wrappedValue: Int
}
extension Wrap: RawRepresentable {
    var rawValue: String {
        wrappedValue.description
    }
    init?(rawValue: String) {
        guard let value = Int(rawValue) else {
            return nil
        }
        wrappedValue = value
    }
}

struct Test {
    @AppStorage("x") @Wrap var x: Int = 5
}

var test = Test()
test.x = 6
print(UserDefaults.standard.object(forKey: "x"))    // wonderful: it worked

print(type(of: test.$x))    // problem: this is `Binding<Wrap>`, how to access `Binding<Int>` instead?

// in my actual code, I'm using this solution to work with `Color` like this
// `@AppStorage("myColor") @ColorRawRepresentable var color = Color.blue`
// I need to get `Binding<Color>` to pass into `ColorPicker`

Ref: "Should not extend type you don't own with protocol you don't own" is the advice, but - #4 by jrose

@hborla ?

$$x? I think wrapper composition simply uses the markers for each layer of wrapper.

Sorry I forgot to mention, I tried that:

print(type(of: test.$$x))   // Error: Type of expression is ambiguous without more context

Edit: okay, if there is no straight forward answer, then I can just make a Binding manually:

Binding(get: { x }, set: { x = $0 })

I think it should work....

Edit: Yes, it worked:

let binding = Binding {
    color
} set: {
    color = $0
}
ColorPicker("Color", selection: binding)

Ah. I think you’ll need to project the binding in that case ($x.wrappedValue). AppStorage doesn’t know about any additional nesting, so it has to vend what it’s given as its generic argument. That’s back to your “manual wrapper struct”, I know, but only in this case.

This is just Wrap

Ah, I was hoping it would use the dynamic subscript. You can either invoke that directly or add an alias to Wrap that won’t get shadowed by Binding.

I just manually create a Binding<Color>: