The documentation for init(wrappedValue:) indicates that you should never call it. It is used by the declaration of state property:
Discussion
You don’t call this initializer directly. Instead, declare a property with the @State attribute, and provide an initial value; for example, @State private var isPlaying: Bool = false .
I saw the "discussion". I still like to know what's the difference between the two. Despite "don't call this initializer directly", you can call it anyway and I see code that calls this.
Anyway, init(wrappedValue:) "don't call directly", init(initialValue:) does not say this. There must be some difference? What is it?
init(initialValue:) was the first name chosen when the original Property Wrappers proposal was released. It was changed to init(wrappedValue:) but the original one was left for backwards compatibility.
That's my initial thought, but doesn't the SE-0258 review ends a good while before the announcement of SwiftUI, and most definitely long before the beta test ends?
I don't know where you got this from as this isn't correct. You can and must use it if you need to initialize the property wrapper by hand. If your PW has an init(initialValue:) overload, you should just ignore it and prefer init(wrappedValue:), that's all.
In Combine framework, they had this issue for a whole year. Basically they shipped with only init(initialValue:) but added init(wrappedValue:) a year later and also back ported it using @_alwaysEmitIntoClient.
According to Swift Programming Language Guide, Swift can use init(wrappedValue:) to initialize a property wrapper when you apply the wrapper to a property. For example, if you created a property wrapper named SmallNumber, you can do this in code:
struct BigRectangle {
@SmallNumber var width: Int = 1
@SmallNumber var height: Int = 1
}
In the code above, when you write = 1 on a property with a wrapper, that’s translated into a call to the init(wrappedValue:) initializer by Swift.
According to the documentation, you should use init(initialValue:) to create a state with an initial value.
That is an API contract not a general PW thing. State has some special framework specific behavior which they communicate through documentation and WWDC videos. If you use the init to inject value from a parent, you will tap into bugs.
The following example is totally valid:
@propertyWrapper
struct Wrapper {
let wrappedValue: Int // `init(wrappedValue:)` synthesized
}
struct T {
@Wrapper
var a: Int = 0
@Wrapper
var b: Int
init(b: Int) {
self._b = Wrapper(wrappedValue: b * 2)
}
}
Long story short, ignore init(initialValue:) on property wrapper types that has been already shipped. It's likely you never will see such an init on future property wrappers unless it provides the user some totally different semantics.
Except we have to use direct initialization if we want to provide an initial value. Initializing just the wrapped value doesn't trigger the wrapper's effects. Is there an alternate solution here?
Say you have a View with a Picker inside and want to set the initial selection from outside the view. Just passing the selection and setting it on the state property doesn’t work, but initializing the State wrapper directly does.
That‘s an anti pattern anyway because of the re-injection for State. You should create a model from a different place and pass it down. Again, that‘s not a property wrapper issue.
struct MyView: View {
class Model: ObservableObject {
@Published
var selection: Int
init(selection: Int) {
self.selection = selection
}
}
@ObservedObject
var model: Model
var body: some View {
Picker(selection: $model.selection, ...)
}
}
Initialize the model from the parent for example using StateObject to persist it, pass it down and use the property wrappers projected value to obtain a binding.
So to merely set the default value of a Picker we need to create a separate type, own an instance outside the view that cares about it, inject it into the view, and use that binding? Even with the new @StateObject, that doesn't seem right.