Allow Property Wrappers with Multiple Arguments to Defer Initialization when wrappedValue is not Specified

One of the use-cases this would work nicely for is Codable wrappers if the wrapper has other properties you want to specify inline, but would only be initialized when an auto-synthesized init(from decoder:) assigns a value to the property, a simplistic example to illustrate would be (Formatted is a made-up wrapper here):

struct MyType: Decodable {
   @Formatted("hh:mm") var minutes: Date
   @Formatted("yyyy-MM") var months: Date
}

where you cannot assign a single date decoding strategy to a decoder.

This was something I was looking into before, but couldn't solve it without manually spelling out a decoding initializer.

10 Likes

Dream of getting it in Swift 5.4 :)

I started using property wrappers about a week ago and really like them, and I couldn't figure out how to get this particular aspect of them working and I figured there was something I was definitely misunderstanding. I was about to launch a detailed question on StackOverflow about this when my search results took me to this exact page, so I guess I'm not missing anything after all! I would definitely like to have wrappers allow for this basic functionality: a wrapper that takes additional arguments but does not require a default value to get the same initialization semantics as normal properties.

2 Likes

Is there any way to get some movement on this? I'd love to see this improvement too - I was reminded of this limitation today whilst working on my own encoding/decoding library.

2 Likes

+1 this would help simplify some code in a package we are developing that wraps a very large C library in Swift.

This would be really great – now that we're able to use property wrappers in function and closure parameters, this would go even further in improving the model. For instance, this could become a really valuable feature for @mbrandonw's and @stephencelis 's Composable Architecture which is becoming increasingly popular (and for good reason!). For SwiftUI views, in TCA you pass down the store as a property, and then at each view create a "view store" which chisels away the store and the store's state to the bare essentials a view needs to do the job, for both ergonomics and performance. You can either do this using WithViewStore, or by using an @ObservedObject var viewStore and custom initialisation logic.

However, both of these methods have their own issues, which are talked about in this thread. If we were able to defer initialization like proposed here, a new and really expressive syntax could be enabled:

@ViewStore(state: ViewState.init) var store: Store<AppState, AppAction>

or

@ViewStore(state: \.someState, action: AppAction.someAction) var store: Store<AppState, AppAction>

while being able to pass down the store from the parent view.
As @hborla specified, this hopefully should be straightforward to implement, and could enable some really great APIs.

Big +1

+1 — this would be a huge boon to the usefulness and ergonomics of property wrappers

1 Like

+1

1 Like

+1 … in my opinion this is a good chance to get rid of some boiler plate. Often we find some workarounds for things we need to do often. I like the approach of making things more convenient as long as it does not contradict a general understanding of how code “should” look like.
I think this proposal is a good example of how a little change can make a difference.

4 Likes

Agreed!

1 Like

Any possibility of getting some more traction on this? Although I’m planning to, I don’t have time right now to learn C++ and the compiler to implement this feature myself, but it seems like it could be really useful for many people, and a natural extension of property wrappers.

2 Likes

Hey everyone, I'm currently working on the implementation for this pitch and we will hopefully graduate this to a proposal very soon!

20 Likes

Any news on this, @amritpan?

4 Likes

+1 would be really useful

It's been two and a half years now… @amritpan you still working on that?

The plan was to integrate this into another pitch involving property wrapper initialization and have it go through the review process together. Unfortunately, I ran aground in the implementation of the other pitch and that has held this up.

I certainly understand the eagerness to get this integrated into the language! I will attempt to put up the implementation for this specific feature under the experimental flag later this month.

10 Likes

Yes, please add this. Yesterday would have been great. ;)

I was about to post another thread because I had something like...

struct ContentView: View {
    @InjectedObject(\.contentViewModel) var model: ContentViewModel
    var body: some View { ... }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        let model1 = ContentViewModel(test: 4)
        ContentView(model: model1) // FAILS WITH TYPE CONVERSION ERROR
    }
}

The keyPath injects from a container, but I also want to be able to pass test versions into the view from the preview code as shown.

I'd done something similar with StateObject prior to this, but the two part initialization with the keyPath AND the wrappedValue simply doesn't work.

I can do this If need be...

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        let model1 = ContentViewModel(test: 4)
        ContentView(model: InjectedObject(model1)) // WORKS... BUT CLUNKY
    }
}

+1 for this feature!

Any update on this? It's been 3 years. It is much wanted and great improvement.

3 Likes

Yes, this will unblock lots of useful possibilities around wrappers