I have a view, where i can add a new entry to CoreData. The name for that entry cannot be null, which can be seen in the ViewModel. If someone tries to add a new entry without a name, they are presented with an error. Now, every time the error pops up, it dismisses itself.
The View:
struct AddProductPopover: View {
@Environment(\.presentationMode) var presentationMode
@StateObject var prodPopViewModel = AddProductPopoverViewModel()
var body: some View {
NavigationView {
List {
HStack {
Label("", systemImage: K.ProductIcons.name)
.foregroundColor(.black)
Spacer().frame(maxWidth: .infinity)
TextField("Add Name", text: $prodPopViewModel.newProductName)
.keyboardType(.default)
}
}
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("Save") {
prodPopViewModel.saveProduct()
// if saving fails due to an empty name, the dismissal is still called before the error is displayed
presentationMode.wrappedValue.dismiss()
}
.alert(isPresented: $prodPopViewModel.showAlert) {
Alert(
title: Text("Product Name cannot be empty!"),
message: Text("Please specify a name for your new Product.")
)
}
}
}
}
}
The ViewModel:
class AddProductPopoverViewModel: ObservableObject {
var managedObjectContext = PersistenceController.shared.container.viewContext
@Published var newProductName: String = ""
@Published var newProductVendor: String = ""
@Published var newProductCategory: String = ""
@Published var newProductStoredQuantity: Int = 0
@Published var showAlert = false
func saveProduct() {
// if name is not nil saves the new product to CoreData
if !newProductName.isEmpty {
let newProduct = ProductEntity(context: managedObjectContext)
newProduct.productName = newProductName
newProduct.id = UUID()
newProduct.productVendor = newProductVendor
newProduct.productCategory = newProductCategory
newProduct.productStoredQuantity = Int32(newProductStoredQuantity)
PersistenceController.shared.save()
} else {
showAlert = true
}
}
I have figured out, that issue lies in the View in the Button Save action. Whenever the check in the ViewModel fails, it sets the boolean required for the alert to true. However, after setting that boolean to true, it returns to the view first and completes the next step in the Button Action, which is dismissing the current view before it then finally triggers the Alert. This execution order results in the Alert to be dismissed. However, the alert should not be dismissed. Dismissing should only happen if saving to CoreData has been successfull.
Button("Save") {
prodPopViewModel.saveProduct()
presentationMode.wrappedValue.dismiss()
}
What changes would I need to make to skip the dismissing line in case the boolean is set to true? I thought of including the dismissal in the ViewModel. However, that would violate the MVVM concept I'm trying to follow.
Replace save button with: