These two syntax’s are equivalent. They both do the some to the @State/@StateObject property wrapper. It’s fine to do this as long as you know the view’s lifetime will not caused the view to be re-created: views can be re-created due to (why do view get re-created?) whatever.
I wish I know what the lifetime of a view is and how and why views have lifetime.
There are a few ways to debug view lifetimes. You can add the "magic" line let _ = Self._printChanges() inside your view's body to see when and why it gets (re)evaluated. When the printed message says the identity changed, that's when any @State properties are captured to the view hierarchy and @StateObjects are lazy-initialised.
You can also get an idea of view identity by adding the wrapped property @Namespace var namespace to the view. Sometimes I prepend it to Self._printChanges() with any extra info about the view which helps me debug things like a view's title or URL or item ID, something like:
var body: some View {
let _ = print(self.title, self.namespace, terminator: " -- ")
let _ = Self._printChanges()
...
}
That little terminator helps keep the info on the same line.
When we talk about view lifetime, it's not the lifetime of the view struct but of a node in a view graph hidden inside SwiftUI's internals. AFAICT the @Namespace wrapped property refers to the ID of that node.
I believe it's because StateObject applies private(set) modifer to its wrapped value. The code demonstrates the same behavior.
@propertyWrapper
struct DummyWrapper<T> {
private(set) var wrappedValue: T
public init(wrappedValue: T) {
self.wrappedValue = wrappedValue
}
}
struct Test {
@DummyWrapper var x: Int
init(x: Int) {
// This works
// _x = DummyWrapper(wrappedValue: x)
// Error: cannot assign to property: 'x' is a get-only property
self.x = x
}
}
So I think the statement in SE-0528 @ole quoted above is true, as long as the wrapped property is settable in init(). That said, I also only use the underscore syntax because it's explicit and always works.
Note: as pointed by others above, just because PW initialization code compiles doesn't necessarily means it does what the user intends to do. But that's PW specific behavior.
If I understand correctly, this is a different case because the property has already been initialized before init is run. Because Swift treats this:
@State var value: Int?
as if you had written this:
@State var value: Int? = nil
The property is already initialized when init runs, so it makes sense that init accesses the setter. But this is not the case in our example in this thread.
I agree this looks like a bug. I don't see a reason why self.x = x would be re-written to a setter call rather than re-written to property wrapper initialization. Please feel free to file an issue at Issues · apple/swift · GitHub!
That’s because the T? @State var sometimes are not auto initialized to nil and so later inside init the var can be assigned and you are saying this should be an init. Either ways, something odd is happening that allow a T? to init,table inside init. This should be allowed and you should use Optional as type.