I've been digging into Property Wrapper initialization trying to understand what's happening under the hood. I ran across this initialization ordering issue that's confusing me. This is kinda a follow up to a question I asked earlier.
@propertyWrapper
struct Untagged<Value> {
var wrappedValue: Value
}
@propertyWrapper
struct Tagged<Value> {
init(name: String) {}
var wrappedValue: Value { get { fatalError() } set {} }
}
// NOT OK
struct Foo1 {
@Tagged(name: "A") var a: Int
@Untagged var b: Int
init(a: Int, b: Int) {
self.a = a // 'self' used before all stored properties are initialized
self.b = b
}
}
// OK
struct Foo2 {
@Tagged(name: "A") var a: Int
@Untagged var b: Int
init(a: Int, b: Int) {
self._a.wrappedValue = a
self._b = Untagged(wrappedValue: b)
}
}
// OK
struct Foo3 {
@Tagged(name: "A") var a: Int
@Untagged var b: Int
init(a: Int, b: Int) {
self.b = b
self.a = a
}
}
// OK
struct Foo4 {
@Tagged(name: "A") var a: Int
@Untagged var b: Int
init(a: Int, b: Int) {
self._b = Untagged(wrappedValue: b)
self._a.wrappedValue = a
}
}
Why isn't Foo1.init equivalent to Foo2.init? Both access _a first, which should be an initialized Tagged(name: "A").
while the (optional) init(wrappedValue:) enables initialization of the storage from a value of the property's type
Wow, there's so much compiler magic around the existence certain property wrapper inits.
It seems a little unfortunate that it's required for this example, since you might want to require a name value and don't have a good fallback. It'd be nice if somehow init(wrapperValue:name) could satisfy that requirement.