Default init internal needs a workaround. Enough is enough

Yes, that's the SwiftUI magic. Trust me. The framework identifies the storage location from the access pattern when constructing the view hierarchy, not the object identity. State is a struct, it doesn't even have a sense of identity.

Or do you mean that the second initial value is thrown away, then yes, the initial value is discarded in subsequent creation. You should provide the same value every time.

State is a struct that wraps a reference, and reading the wrappedValue reaches into the persistent underlying reference. When DashboardView is initialized the second time, the new value is not shoved into the persistent store; it is dropped on the floor.

Ok, so you mean this:

1 Like

Yep, and the point of what I'm trying to say is that setting @State values explicitly in an initializer breaks the expectations for how data should flow through a SwiftUI hierarchy :smile:

In all seriousness, it does have a very consistent behaviour. Though most of the time you'd want to synchronize State during view events, like onAppear instead of the view initialization. So I find the manual initialization abhorrent on almost every occasion anyway.

1 Like

Any consistent behavior you see is an accident. @State's initial value must be independent of any instance of the view. Chances are good this will be enforced at compile time when the necessary property wrapper functionality exists to do so.

11 Likes

Even if it’s not the intended way to initialize @State, it’s the obvious approach most users will reach for and far lighter weight than creating some sort of ObservableObject as storage just to be able to set an initial value.

Is that documented anywhere? I just re-read some of the documentation and I couldn't find it called out explicitly. I can't tell you how times I've heard it recommended to initialize state that way. If that behavior is discouraged, it may be worth explicitly calling it out because, at least in the swath of the iOS community I've interacted with, that's a pretty widely accepted way of doing things.

2 Likes

The cases where it will appear to work are in cases where you know the view will only really be created once, e.g. in a top level of a sheet, or an “editor” view where it’ll be removed from the hierarchy after editing. If you’re relying on the fact that view won’t be re-initialized, it’s probably fine?

I’ve used this pattern in exactly these circumstances, i.e. in the top-level view in a sheet where the initial state comes from the outside, but where the state is thereafter owned and managed by the view. Why does this only ”appear to work”? What could go wrong that I’m not aware of? And what should I use instead?

wow.. just... wow. I expected to come back to a bunch of replies about.. you know.. the actual problem I posted (default init scope) and I come back to a hijacked discussion about swiftui state var behavior. Do I need to start a new post all over again? This is why swift's official github repo needs an issues section instead of this forum.

3 Likes

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)

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.