I have an app where I have several buttons whose actions are shielded by a confirmation dialog. For example:
@State private var confirmDeleteAll: Bool = false
var body: some View {
// ...
Button {
confirmDeleteAll = true
} label: {
Label("Delete all", systemImage: "trash")
}
.confirmationDialog("Delete all data", isPresented: $confirmDeleteAll) {
Button("Delete all", role: .destructive, action: deleteAll)
} message: {
Text("This will wipe all data in the app")
}
// ...
}
These all work fine, and the button in the confirmation dialog shows up in red as expected (seen here on an iPad):
But as I have a load of these, I'd like to refactor the code to make the pattern a little simpler. My principle in the refactor is:
- Create a button with the ultimate action to take defined in the button's
action
argument. - Apply a custom button style which replaces the button's action with displaying a confirmation dialog - inside which is a destructive button that, when pressed, performs the supplied action
That gives me code that looks like
struct ConfirmationButtonStyle: PrimitiveButtonStyle {
var title: LocalizedStringKey
var message: LocalizedStringKey
init(_ title: LocalizedStringKey,
message: LocalizedStringKey = "") {
self.title = title
self.message = message
}
@State private var showConfirmationDialog: Bool = false
func makeBody(configuration: Configuration) -> some View {
Button(role: configuration.role) {
showConfirmationDialog = true
} label: {
configuration.label
}
.confirmationDialog(title,
isPresented: $showConfirmationDialog) {
Button(role: .destructive, action: configuration.trigger) {
configuration.label
}
} message: {
Text(message)
}
}
}
extension PrimitiveButtonStyle where Self == ConfirmationButtonStyle {
static func confirm(
_ title: LocalizedStringKey,
message: LocalizedStringKey
) -> some PrimitiveButtonStyle {
ConfirmationButtonStyle(title, message: message)
}
}
My refactored button then becomes:
Button(action: deleteAll) {
Label("Delete all", systemImage: "trash")
}
.buttonStyle(.confirm(
"Delete all data",
message: "This will wipe all data in the app"
))
From a functional standpoint this works fine. Visually, though, despite the confirmationDialog's button clearly being marked as destructive, its colour reverts to my application's accentColor
, which in this case is purple:
From a code point of view I can't see why the destructive
role would not be observed. Am I missing something basic here?