SE-0258: Property Wrappers (second review)

Took four read throughs mostly due to being insure about the $ syntax.

Overall I think this fits well. It does feel Swifty, but different. The different always concerns me, but I think the changes are justified with valid use cases.

This proposal feels more solid and holistic.


Not wrappedValue, but the backing storage. :slight_smile:
wrappedValue is accessible without any $.

Of course, you're right.

All the renaming of things between versions is making my head hurt. As is the single character difference between wrapped/wrapper.

Overall I am for this feature being accepted. However, I wish to complain about the color of one of the bikesheds, so please bear with me.

With subtle and fine distinctions, such as between wrapperValue and wrappedValue, careful attention needs to be paid to documentation and language -- how users will come to know and experience this distinction. I'm concerned that people will come to understand this genuinely useful and laregly non-magical feature as its opposite. I dont have any particular solutions, but instead I offer that one of the big contributors is a consequence of the fact that @propertyWrapper is built-in: the interface it asks of implementors is not declared anywhere with in-language constructs.

It's probably only because I was messing with some OCaml and ReasonML exercises recently, but it seems like Swift keeps reaching towards different syntactic forms that could be expressed with modules or module "functors". Not just property wrappers but function builders, variadic generics, parameterized extensions, even higher-kinded types. In this case, having @propertyWrapper become merely a module functor would clarify exactly the different signatures clients could implement.

Thanks for your time & attention.

tl;dr +1 on the feature, -0 on wrappedValue / wrapperValue, pls to haveing modulez


Note that there are toolchains that implement the proposal:

Expect bugs! And when you find them, please report them at



The backing storage access level rules need more thought before we should consider accepting this proposal.

Proposed Change

I suggest we make the following change to this proposal:

- • Reduced the visibility of the synthesized storage property 
-   to private.
+ • Synthesized storage properties have the same access level
+   as the base property 
- • When a wrapper type provides wrapperValue, the (computed)
-   $ variable is internal (at most) and the backing storage variable 
-   gets the prefix $$ (and remains private).
+ • When a wrapper type provides a wrapperValue, the backing
+   storage gets the prefix $$, and wrapperValue is accessible with
+   $ prefix


I've been following this proposal since day one and I'm a huge fan. I'm extremely grateful for the amazing work Doug and others have put into this. Property wrappers will open the door to tons of new functionality in Swift and I can't wait to use them in the OSS packages I maintain.

However, we really need to simplify the access level rules. I understand wanting to be conservative with access level as a first step, but I think simplicity and usability are much more important. Especially considering the future direction of property wrappers, these complex access level rules will be difficult to work around.

For example, here is good use case for property wrappers that is not possible with the current state of access control:

Module A:

public final class User: Model {
    @Field public var id: Int?
    @Field public var name: String

Module B:

import A

// error: $name is not accessible
User.query(on: database).filter(\.$name == "Foo").all()
//                                ^

As a developer, this error is confusing to me. I've marked everything as public, why can I not access the properties on my type? Is there any way to make the backing storage accessible? As of this current proposal, the only way to achieve this would be:

public final class User: Model {
    @Field public var id: Int?
    public var idField: Field<Int?> { 
        return self.$id 
    @Field public var name: String
    public var nameField: Field<IString> { 
        return self.$name 

This is obviously far from ideal. It would be easier to not use property wrappers in the first place.

Suppose we do add a way to control access level in the future, how would we make the wrapper public? Maybe something like this:

public final class User: Model {
    @Field public(wrapper) public var id: Int?
    @Field public(wrapper) public var name: String

This would also be confusing to me. Why do I need to declare public twice on this property? It would make much more sense to opt-in to restricting access:

public final class User: Model {
    @Field private(wrapper) public var id: Int?
    @Field private(wrapper) public var name: String

Looking at this API, it's much clearer what is happening and makes more sense: This is a public property, so if I want to make part of it private, I should specify that.

Part of what makes Swift so great is its "simple by default" philosophy. For example, you can declare var x = 5 without needing to specify a type. Swift will do what makes sense by default. However, when you need more complexity in Swift, you can always opt-in: var x: UInt32 = 5. Same goes for all of Swift, but there is even precedent with access level here:

final class Foo {
    public private(set) var x: Int

Again, simple by default, with the possibility for more complex rules if you need. Why shouldn't property wrappers behave similarly?

Finally, I want to note that the latest change to have wrapperValue be internal by default is a step in the right direction. This at least unblocks a lot of potential use cases for this feature. But let's go one step further and remove the complexity and restrictions entirely.

There are a ton of awesome ideas popping up for property wrappers: @Lazy, @UserDefault, @Atomic, just to name a few. All of these wrappers have useful properties and methods on the backing store. Please let developers access this functionality!



Though I am not at all a fan of how close wrappedValue is to wrapperValue. the distinction is in the middle of the identifier. This will be frustrating to teach and frustrating to remember.


Also +1 on this issue. I'm not seeing a great reason to have property wrappers be limited in this way. If users want to protect the storage then they can specify the access level, that way it reads as an explicit action/choice that differs from the default and that brings clarity.


Very loudly agreed. I'm -1 on this proposal until this issue is addressed.


Can't one effectively raise the visibility level of the synthesized property simply by declaring a (public) wrapperValue getter that returns Self?

No, more complex than that. You can raise it from private to internal, but not past. There’s no way in the current proposal to make the backing store public.

1 Like

I found a few places in the proposal text and examples which look like inconsistencies. If they are, I think it would worth fixing them to help people understand this fairly complex proposal:

Proposed solution

var $foo: Lazy<Int> = Lazy<Int>(initialValue: 1738)
var foo: Int {
  get { return $foo.value }
  set { $foo.value = newValue }

Shouldn’t the wrapper property be private?

Composition of property wrappers

var $path: DelayedMutable<Copying<UIBezierPath>> = .init()
var path: UIBezierPath {
  get { return $path.value.value }
  set { $path.value.value = newValue }

Same comment as above.

Initialization of synthesized storage properties

This section does not mention the initialization that allows declaring the wrapper with arguments while also receiving the initial value through the property declaration. So there are technically four ways to initialize a property wrapper.

Secondly, this section also mentions:

When there are multiple, composed property wrappers, only the first (outermost) wrapper may have initializer arguments.

This sounds weird because I don’t see why the limitation exists. It is also in contradiction with an example a few sections below where it’s the last inner most wrapper that has initialized argument.

@A @B(name: "Hello") var bar = 42
// type inference as in ...
var $bar = A(initialValue: B(initialValue: 42, name: "Hello"))
// infers the type of '$bar' to be 'A<B<Int>'

Detailed design

I’m if I’m not mistaken, the proposal doesn’t clearly state that the generated property wrappers are private. If that’s the case, could a small section be written about that under Detailed design?


As to the proposal in itself, I am super happy with how much it has improved since it’s earlier iterations. I’m especially delighted about wrappers being able to be declared with arguments as well as being initialized with the initial value.

Nonetheless, I have the feeling that it would be important to address the accessibility level question before we bake in a private default. Like @tanner01, I would really prefer the wrapper (and wrapperValue) to default to the same accessibility level as the property and allow tweaking them with a private(wrapper) modifier.


Could you clarify what is synthesised in the various cases. For example, in the UserDefaults case it seems we don't need any stored properties at all. The simple value is always returned through a computed property for all cases (right?) and for UserDefaults the storage doesn't require any stored property either since it is also computed. Is there a way to avoid creating stored properties? That would be very convenient when extending types. Though that could lead to abuse.

I am (still) very very +1 on this feature and direction, thank you for diligently pushing this forward and iterating. The changes are mostly all very sensible, this is a great step forward! Also, thank you for providing a "diff" in the second review notes, that is very helpful.

Most of the changes are great, but I'm confused about the motivation for the $$ / wrapperValue stuff: it is complicated, confusing, and makes the user side syntax worse. I'd love to see more elaborate explanation of why this is an important addition to the proposal, because honestly I'm just confused and don't get it.

If it turns out that we really do need "two things" on the caller side, then I recommend making these changes:

  • Move the prefix x.$foo thing to use postfix $ for the normal wrapped thing, so we have and$. We can explain the $ as meaning "storage".
  • Move the x.$$foo thing to have a name, e.g.$something where "something" is a word that describes what this thing is (I'm still confused, so I can't come up with a concrete suggestion).

This achieves several goals: 1) it allows an using a word for the thing instead of yet-another magic dollar sign. 2) it makes these things work better in code completion, 3) it provides room for future expansion when (in the future) the proposal is inevitably feature crept even further forward 4) it solves the LLDB naming conflict.

On the declaration side, I find the names wrapperValue and wrappedValue to be so close to be confusing and easy to mistake. If we need to have two, it would be great to make them be more lexically different. I'm not sure why value got renamed (some oblique mention about a name collision)?

Caveat that I don't really understand why we need this thing in the first place, but I find this access control answer to be super confusing and weird. I thought we agreed to make it private - not make one thing private and add a new non-private thing?

What is the use-case? This proposal is obviously motivated by SwiftUI - can you add SwiftUI examples to the proposal to be more transparent and direct? It would make this so much easier to understand what you're actually achieving and why.

Yes. Yes. N/A. Tons.



I like this direction. Right now, as I see it, Property Wrapper is doing 2 things.

  • Add common getter and setter boilerplate. (via wrappedValue)
  • Create access to relatedValue of a property. (via wrapperValue)

Both are very close to each other, and it's almost impossible to add them both orthogonally (at least, I think), perhaps because both features use base storage.

Anyhow, I feel like base storage is not exactly usable on its own. It's very common to use function/accessor on it. So I think suffix $ would work well here.

While wrapperValue (2nd feature) is generally useful as a variable on its own, and you may not even need to access the variable's function/accessor. Prefix $ would be appropriate here.


struct Wrapped {
  var wrappedValue: ... { ... }
  var wrapperValue: ... { ... }

  func someWrapperFunction() { ... }

@Wrapped var foo = ...

foo$someWrapperFunction() // This access baseStorage.someWrapperFunction
foo$self // This access baseStorage.self
$foo // This access wrapperValue

As I think baseStorage and wrapperValue have different use cases, this should work.

If using suffix only on baseStorage doesn't work, using suffix on both of them doesn't seem like a bad idea either.

1 Like

See @tanner0101's comments above for a non-SwiftUI example of why just making the backing store private is not a viable solution.


Why not just use \.name?


My suggestion:

wrapperValue should be renamed to wrapperBinding and that should be the only thing exposed via $. If i need access to the backing storage I should be able to access via $someProp.wrappedValue

1 Like
  • What is your evaluation of the proposal?

I love the idea of this proposal and it definitely seems to be well refined at this point. I'm willing to accept this as is.

  • Is the problem being addressed significant enough to warrant a change to Swift?

Yes. I often find myself reaching for a solution like this. It's a great example of removing boilerplate and making code easier to read and reason about. The fact that it has the potential to replace several builtin features is another clue that it's a good idea.

  • Does this proposal fit well with the feel and direction of Swift?


  • If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

Nothing comes to mind.

  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

I've read the proposal (and some previous versions of it) and experimented with the Xcode 11 beta and with SwiftUI.

My biggest complaint about this proposal is delegateValue/wrapperValue. It seems kind of magical and non-vital. I can see why SwiftUI would want this feature, but it took me forever to figure out how State was being magically transformed into a Binding, even after reading an earlier version of this proposal. This seems like something that is going to make the language harder to understand, even if it makes SwiftUI easier to learn. I would be in favor of renaming it to something longer and more self documenting, or eliminating it altogether (and forcing things like SwiftUI to use $foo.binding).

I'm also disappointed to see that referencing the enclosing self isn't possible in this iteration. One use case I'd like to use this for is to replace @NSManaged with different behaviors, but that would require having access to methods on the enclosing object. The proposed solution for that in future directions looks good to me though.

I'd also like to see more clarification on how this interacts with Codable. The other generated conformances seem obvious enough, but could this be used to do lightweight customization of the Codable process? For instance, changing how a single property gets encoded/decoded without implementing Codable for the entire type? Could you customize the key that it used like: @Codable(key: "user_name") var name: String?

Terms of Service

Privacy Policy

Cookie Policy