I'm not sure why you would want to have an id
that changes every time you access it, but if you just want to initialise isDisabled
, you can write your own init
, and assign an AppStorage
to _isDisabled
there.
struct ButtonData {
let level: String
let bgColor: String
let image: String
// note that nothing is passed to @AppStorage here
// and this property is not initialised to anything - we'll do that in init
@AppStorage var isDisabled: Bool
init(level: String, bgColor: String, image: String) {
self.level = level
self.bgColor = bgColor
self.image = image
// this is where isDisabled is initialised to false,
// and the AppStorage with the level as key is created.
self._isDisabled = AppStorage(wrappedValue: false, level)
}
}
That said, your view won't update if you want to use it like this:
struct ContentView: View {
@State var buttonData = ButtonData(level: "foo", bgColor: "bar", image: "baz")
var body: some View {
Text("Foo")
.onTapGesture {
// buttonData.isDisabled.toggle()
}
Button("Button") { ... }.disabled(buttonData.isDisabled)
}
}
Because AppStorage
has a nonmutating
setter, so setting it won't cause buttonData
in the view to mutate, and so SwiftUI doesn't know that your view needs updating.
So if you want AppStorage
to update views, you need to put it directly in a View
. For example, you could make your own button view that takes in a ButtonData
, and sets its disabled status accordingly:
// ButtonData can simply be
struct ButtonData {
let level: String
let bgColor: String
let image: String
}
struct MyButton: View {
let data: ButtonData
@AppStorage var isDisabled: Bool
init(data: ButtonData) {
self.data = data
self._isDisabled = AppStorage(wrappedValue: false, data.level)
}
var body: some View {
// perhaps also provide init parameters for the action...
Button {
...
} label: {
Image(data.image)
}
.disabled(isDisabled)
}
}
Now whenever the superview updates the AppStorage
, MyButton
will update its disabled status too.