PropertyWrapper cannot get `wrappedValue` property from protocol/superclass

I was trying out sharing logic between propertyWrappers today when I came across some errors I don't fully understand. Both of the implementations I tried result in the same error about the propertyWrapper object not having a wrappedValue property, however it is provided by a protocol extension/superclass. If I remove the @propertyWrapper from the struct/class I am able to access wrappedValue in both of the examples below.

My first attempt put the shared logic in a protocol extension:

protocol P {
  associatedtype Value
  var rootValue: Value { get }
}

extension P {
  var wrappedValue: Value {
    return rootValue
  }
}

@propertyWrapper
struct S: P { // Error: Property wrapper type 'S' does not contain a non-static property named 'wrappedValue'
  var rootValue = "Value"
}

My second attempt used class inheritance and gets the same error:

class C1<Value> {
  var rootValue: Value { fatalError() } // fatalError to make simplified version work
  var wrappedValue: Value {
    return rootValue
  }
}

@propertyWrapper
class C2: C1<String> { // Error: Property wrapper type 'C2' does not contain a non-static property named 'wrappedValue'
  override var rootValue: String { "Value"}
}
Real world details

In the examples above I could rename rootValue to wrappedValue and it would make everything work, however that creates a different issue for the real problem that started my investigation.

What started this was trying to create multiple propertyWrappers around SwiftUI's Environment propertyWrapper. The reason I tried the above protocol/class approaches is because renaming the variable requires a custom init.:

@propertyWrapper
struct S { // Error: 'init(wrappedValue:)' parameter type ('Environment<Locale>') must be the same as its 'wrappedValue' property type ('Locale') or an @autoclosure thereof
  @Environment(\.locale) var wrappedValue
}

Adding a custom init (init() {}) resolves the error, but defeats the point of reducing boilerplate.

Is anyone able to explain why these don't work and/or a way to make them work?

3 Likes