Default init internal needs a workaround. Enough is enough

As I said earlier, no need to be antagonistic. Have you checked out [Review] SE-0018 Flexible Memberwise Initialization, Explicit Memberwise Initializers? Would you like to add anything to the discussion other than your apparent +1?

Even then, IIRC what missing was the implementation, not the design.

2 Likes

Nah you're right. Sorry for the over-the-top response. I'll dive more into the post referenced. It seems still a bit overkill for what I was after, just a basic almost C++ - like scope control over the default memberwise constructor

We also do have a public bug tracker, it's just a JIRA instance instead of github issues.

3 Likes

Btw you can generate the memberwise initializer via Xcode (right click on type > Refactor > Generate Memberwise Initializer) so you don’t have to write one out manually in this case. You can then tweak it as per your needs.

1 Like

That feature is grayed out for SwiftUI structs that have state or bindings

You can re-initialize a View's @State by changing the view's id. I'm not sure this work around is kosher or just happens to work now and will break in the future.

.init(initialValue:) vs. .init(wrappedValue)

1 Like

What, then, is the correct way to create a public initializer for a public view whose state variables are private and where the caller would like to specify an initial value for those variables?

Or are you saying that because they are private, the caller shouldn't know about them, the initializer shouldn't expose them, and that they should always have a default value? And if the caller really does need to know about them and supply a value that they should @Bindings instead of @State?

Multiple SwiftUI views have multiple initializers for multiple purposes. How do I create my own view that has a decent API with multiple initializers and accepts a BINDING in one case and no binding in another? I don't want to write self._binding = binding because it is obviously not an officially correct way, it is underscored.

Using _variable for wrapper storage is official, though.

Must have missed that. But why would anyone assume that doing something with an auto-generated underscored variable is safe?

I'm pretty sure he just meant that if they're gonna take the initial value from outside, each call needs to have the same initial value.

Which part of it is unsafe, though? We pretty much know exactly how the wrapper code is generated, and it (officially) uses underscored name.

Are you sure about that? I never even considered using anything like that. I would guess most people are like me and are too wary of SwiftUI magic to use things in a radically different way from official recommendations.

My point was that there is no official recommendation or anything preventing the use of the initializer like that, so that's what most users will reach for by default. Apple has no obvious examples of initializing a View with initial @State values so unless they do their own research there's no way for them to figure out that @State in SwiftUI is different than other wrappers you might want to initialize.

And I have only seen examples where @State variables have initial values! Like:

@State private var isActive = false

That obviously not what we're talking about here. The question is, how do you provide an initial value to that state from outside the view? Naturally users would reach for the initializer, but that's apparently not what you're supposed to do. That realization is both non obvious and not really documented.

My impression was that you're not supposed to use it like that, that State properties should only be used internally and if you want to pass in data you use a normal let property, or a Binding if you want to change it.

I guess this doesn't really cover the case where you want to get a starting value from the outside, but don't want to bother the outside with any subsequent changes to the value, but for me it would still be more natural to use a binding then. Or simply update the State properties value in the initialiser based on the input (does that work?)

That can work, if the views are directly related. But you still have the non-obvious nature of binding state you don't care about in the parent view. However, once there's more than a single layer between the state and the source of the initial value, it gets especially burdensome to constantly bind and rebind. For instance, I want to provide an initial value to a Picker based on the button that was tapped to navigate to the screen where the Picker is a child view. Outside of the Picker I don't care what the selection is, I just want to provide an initial value. It seems natural to reach for an initializer to provide this value, rather than having to set up a Binding or ObservableObject for that sole purpose.

1 Like

Hm, but wouldn't you normally want to use the picker value somewhere? Isn't the beauty of SwiftUI that you can keep everything in sync all the time, automatically. In such cases I would have a sort of Coordinator as an environment object with a property that you bind directly to the picker.

Perhaps in this case the initial value is separate from the picked value? If so, can't you just pass it in through a var/let property, since you are not going to change it?

It's a SegmentedPickerStyle() picker which reveals one of two views, so it doesn't matter what the selection is, just that we can set it to one of the two options.