0

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):

Working confirmation dialog with destructive button

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:

  1. Create a button with the ultimate action to take defined in the button's action argument.
  2. 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:

refactored button and dialog, with destructive button not showing in red

From a code point of view I can't see why the destructive role would not be observed. Am I missing something basic here?

ScottM
  • 7,108
  • 1
  • 25
  • 42

0 Answers0