I just learned about Swift's property wrappers from Nick Sarno's YT video and they seem pretty cool. I'm investigating and I created a property wrapper to uppercase a string:
@propertyWrapper
struct Uppercased: DynamicProperty {
@State private var value: String
var wrappedValue: String {
get {
value
} nonmutating set {
value = newValue.uppercased()
}
}
init(wrappedValue: String) {
self.value = wrappedValue.uppercased()
}
}
Then I can include it in a view like:
struct ContentView: View {
@Uppercased private var str: String = "pork on the fork"
var body: some View {
VStack {
Text(str)
Button("New string") {
str = "chicken on the knife"
}
}
.padding()
}
}
When I press the button the text updated.
However, when I put the @Uppercased value inside a nested strut which is then out into the main view with @State, the UI does not update, even when the structure conforms to DynamicProperty too.
Uppercased
already has a @State
. By wrapping Model
in another @State
, the view would be observing the "outer" @State
, not the "inner" one that stores the str
. The "outer" @State
can't see any changes when you change the "inner" @State
. It doesn't work for the same reason as why
@State @State private var str = "Foo"
doesn't work.
Removing the outer @State
and just writing
private var model = Model()
in the View
would work. But it will not observe changes to the non-DynamicProperty
s in Model
.
For such general-purpose property wrappers as Uppercased
, I'd suggest not making it DynamicProperty
.
@propertyWrapper
struct Uppercased {
private var value: String
var wrappedValue: String {
get {
value
} set {
value = newValue.uppercased()
}
}
init(wrappedValue: String) {
self.value = wrappedValue.uppercased()
}
}
If you really want a DynamicProperty
, you can create a DynamicProperty
version of the above like this:
@propertyWrapper
struct UppercasedState: DynamicProperty {
@State private var value: Uppercased
var wrappedValue: String {
get {
value.wrappedValue
} nonmutating set {
value.wrappedValue = newValue
}
}
init(wrappedValue: String) {
self.value = Uppercased(wrappedValue: wrappedValue)
}
}
Use @UppercasedState
in SwiftUI structs like View
/ViewModifier
, and use @Uppercased
everywhere else.