Search code examples
iosswiftuikituistoryboardsegueuialertcontroller

Displaying a UIAlertViewController in shouldPerformSegue


In my project, I set up a UITableView whose cells are tied to a segue that takes the user to another ViewController where the data from the cell gets displayed in detail. However, not all cells should perform the segue (e.g. the record presented in the cell is invalid and should not be displayed, so going to the detailing controller is not necessary).

For this task I use the UIViewController's shouldPerformSegue method: (I assume in the following snippet that the second row in the table is the one which shouldn't perform the segue)

override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
    guard let indexPath = tableView.indexPathForSelectedRow else {
        return false
    }

    if indexPath.row == 1 {
        let alert = UIAlertController(
            title: "Invalid record",
            message: "This record is malformed, can't display the data.",
            preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "Okay :(", style: .default))
        present(alert, animated: true, completion: nil)
        return false
    } else {
        return true
    }
}

This leads to some weird behaviour: whenever I tap the second row, the alert is displayed only after a second or two, sometimes even five seconds after tapping, and sometimes it's not being displayed at all until I perform some other UI action like scrolling the table view or tapping again.

After some trial and error I found a solution: the alert will be displayed immediately after tapping the "erroneous" cell when I force it to the main thread:

DispatchQueue.main.async {
    present(alert, animated: true, completion: nil)
}

This is counter-intuitive for me as the prepareForSegue method is already being executed on the main thread. So my question is: where does the UIAlertController's erratic behaviour come from and do I need to force its display on the main thread or am I doing something wrong?


Solution

  • It is not that shouldPerformSegue isn't on the main queue, but rather that this function is involved in the segue process, so it is a bad idea to go presenting another view at this point.

    By using an async dispatch you allow the current segue process to complete (or be aborted in this case) before the presentation of the alert occurs.

    I think that It is a reasonable solution to your problem.