Before Alert became deprecated, I used to manage different Alerts of a View by introducing an AlertState:
enum AlertState: Identifiable {
case associatedIngredient
case confirmDelete
var id: AlertState { self }
}
I had variable in my View:
@State private var activeAlert: FoodItemViewSheets.AlertState?
which I would set the AlertState with:
// Delete the food item
Button("Delete", systemImage: "trash") {
if foodItemVM.hasAssociatedFoodItem() {
// Check if FoodItem is related to an Ingredient
if !foodItemVM.canBeDeleted() {
self.activeAlert = .associatedIngredient
} else {
self.activeAlert = .confirmDelete
}
}
}
... and later:
.alert(item: $activeAlert) {
alertContent($0)
}
This function would then deliver the right Alert:
private func alertContent(_ state: AlertState) -> Alert {
switch state {
case .confirmDelete:
return Alert(
title: Text("Delete food"),
message: Text("Do you really want to delete this food item? This cannot be undone!"),
primaryButton: .default(
Text("Do not delete")
),
secondaryButton: .destructive(
Text("Delete"),
action: deleteFoodItemOnly
)
)
case .associatedIngredient:
return Alert(
title: Text("Cannot delete food"),
message: Text("This food item is in use in a recipe, please remove it from the recipe before deleting.")
)
}
}
As Alert is deprecated, how to get this kind of behavior (two different alerts associated with the same button) to work with the new alert(_:isPresented:presenting:actions:message:) instance method?
You only need one extra @State
for passing to the isPresented:
parameter. The rest are all pretty self-explanatory. You'd just use switch
statements to provide the title, message, and actions as you were already doing. It's just that you are returning View
, instead of Alert
.
@State private var alertPresented = false
@State private var activeAlert: AlertState?
// this flag controls which alert to show in this toy example
@State private var flag = false
var body: some View {
// as an example, I will use a toggle to control which alert to show
Toggle("", isOn: $flag)
Button("Delete", systemImage: "trash") {
if flag {
self.activeAlert = .associatedIngredient
} else {
self.activeAlert = .confirmDelete
}
}
.alert(alertTitle, isPresented: $alertPresented, presenting: activeAlert) {
alertActions(for: $0)
} message: {
alertMessage(for: $0)
}
.onChange(of: activeAlert) { oldValue, newValue in
if oldValue == nil && newValue != nil {
alertPresented = true
} else if oldValue != nil && newValue == nil {
alertPresented = false
}
}
}
@ViewBuilder
func alertMessage(for alert: AlertState) -> some View {
switch alert {
case .associatedIngredient:
Text("Do you really want to delete this food item? This cannot be undone!")
case .confirmDelete:
Text("This food item is in use in a recipe, please remove it from the recipe before deleting.")
}
}
@ViewBuilder
func alertActions(for alert: AlertState) -> some View {
switch alert {
case .associatedIngredient:
Button("OK", role: .cancel) {}
case .confirmDelete:
Button("Do not delete", role: .cancel) { }
Button("Delete", role: .destructive) {
// ...
}
}
}
var alertTitle: String {
switch activeAlert {
case .associatedIngredient:
"Cannot delete food"
case .confirmDelete:
"Delete food"
case nil:
""
}
}