I am trying to find a way to use the new alert(isPresented:error:actions:message:). My motivation is to find a good way to throw a custom error and have a user facing alert using that error.
My issue is that this works when using local bindings but not when using a ViewModel. I don’t see where I am doing anything wrong, so I am wondering whether this is an error I should file a radar for?
This is my custom error, that implements the LocalizedError protocol:
struct CustomError: LocalizedError {
var errorDescription: String?
var failureReason: String?
var helpAnchor: String?
var recoverySuggestion: String?
}
This view is working just fine:
struct AlertViewWithoutViewModel: View {
@State private var presentAlert = false
@State private var error: CustomError?
var body: some View {
Button("Show alert without ViewModel") {
error = CustomError(errorDescription: "Description of the error", failureReason: "It went wrong.")
presentAlert = true
}
.padding()
.alert(isPresented: $presentAlert, error: error) { error in
Button("OK") {}
} message: { error in
if let failureReason = error.failureReason {
Text(failureReason)
}
}
}
}
What is not working is using this ViewModel––and I don’t understand why:
class ViewModel: ObservableObject {
@Published var presentAlert = false
@Published var error: CustomError?
}
struct AlertViewWithViewModel: View {
@State private var vm = ViewModel()
var body: some View {
Button("Show alert with ViewModel") {
vm.error = CustomError(errorDescription: "Fancy VM error title", failureReason: "Some fancy reason.")
vm.presentAlert = true
}
.alert(isPresented: $vm.presentAlert, error: vm.error) { error in
Button("OK") {}
} message: { error in
if let failureReason = error.failureReason {
Text(failureReason)
}
}
}
}
>Solution :
You need StateObject wrapper (which is observer for ObservableObject) instead of State (which for value types),
struct AlertViewWithViewModel: View {
@StateObject private var vm = ViewModel() // << here !!