1

I have a WKInterfaceController with a WKInterfaceTable that lists some events a user has recorded in my app.

A user can tap a row of the table to see more details about that event. To accomplish this, I've overridden contextForSegue(withIdentifier:in:rowIndex:) on the WKInterfaceController that contains the table, so tapping a row modally presents a detail view of that row in a new WKInterfaceController called EventDetailController.

The modal presentation is defined on the Storyboard. I can't use push presentation because the WKInterfaceController with the WKInterfaceTable is a page among multiple instances of WKInterfaceController at the top level of my app.

Here's the main issue:

Within the EventDetailController, there's a Delete button to destroy the record that the table row represents.

When a user taps the Delete button, I present an alert that allows the user to confirm or cancel the delete action.

Once the user confirms the record deletion, I want to dismiss the EventDetailController since it's no longer relevant, because it represents a deleted record.

Here's the IBAction defined on EventDetailController that gets called when the Delete button is tapped:

@IBAction func deleteButtonTapped(_ sender: WKInterfaceButton) {

    let deleteAction = WKAlertAction(title: "Delete", style: .destructive) {

        // delete the record

        // as long as the delete was successful, dismiss the detail view
        self.dismiss()
    }

    let cancelAction = WKAlertAction(title: "Cancel", style: .cancel) {
        // do nothing
    }

    presentAlert(withTitle: "Delete Event",
                 message: "Are you sure you want to delete this event?",
                 preferredStyle: .alert,
                 actions: [deleteAction, cancelAction])
}

The problem is that watchOS doesn't seem to allow this. When testing this code, the EventDetailController does not dismiss. Instead, an error message is logged in the console:

[WKInterfaceController dismissController]:434: calling dismissController from a WKAlertAction's handler is not valid. Called on <Watch_Extension.EventDetailController: 0x7d1cdb90>. Ignoring

I've tried some weird workarounds to try to trick the EventDetailController into dismissing, like firing a notification when the event is deleted and dismissing the EventDetailController from a function that's called from an observer of the notification, but that doesn't work either.

At this point I'm thinking there's some correct way I'm supposed to be able to dismiss a WKInterfaceController, or in other words reverse the contextForSegue(withIdentifier:in:rowIndex:) call, but I don't know what it is.

When I call dismiss() directly in the IBAction, instead of in a WKAlertAction handler, it works fine, but I don't like this implementation since it doesn't allow the user to confirm the action first.

gohnjanotis
  • 6,513
  • 6
  • 37
  • 57

1 Answers1

6

I feel like an idiot, but I figured out the solution.

The answer was in Apple's WKInterfaceController.dismiss() documentation the whole time (emphasis added):

Call this method when you want to dismiss an interface controller that you presented modally. Always call this method from your WatchKit extension’s main thread.

All I had to do differently was call self.dismiss() on the main thread.

Here's my updated code for the delete action, which now works as expected:

let deleteAction = WKAlertAction(title: "Delete", style: .destructive) {

    // delete the record

    // as long as the delete was successful, dismiss the detail view
    DispatchQueue.main.async {
        self.dismiss()
    }
}

Hopefully this will save someone else some troubleshooting time!

gohnjanotis
  • 6,513
  • 6
  • 37
  • 57
  • We had some similar code working fine on watchOS4 which was broken on watchOS5. Adding this solved it for us: `DispatchQueue.main.async { self.dismiss() }` – Luke Sep 20 '18 at 14:47
  • 1
    Thank you, saved me some time. Also Apple at least could crash the .dismiss() code when it is not called on main thread (like it does in many other cases). – interrupt Jul 30 '19 at 03:40