others-how to solve 'UIAlertController not showing(display)` error in xcode swift?

1. Problem

When I try to show a dialog in my iOS app like this:

@objc func settingsClicked(_ sender: UIButton) {
        let menuViewController = PopMenuViewController(sourceView:sender,actions: [
            PopMenuDefaultAction(title: "test2" , image: UIImage(named: "icon"),didSelect: { (PopMenuAction) in
                UIUtils.shared.showConfirm(controller: self, message: "msg1") {
                    DispatchQueue.global().async {
                        MessageService.shared.updateMessageReadAll() {//update
                            self.loadMessages(queryLatest: false,failedHandlerx: nil) {
                                DispatchQueue.main.async {
                                    self.tableView.reloadData() //reload tableview
                                }
                            }
                        }
                    }
                }
                
            })
        ])
    }

You can see that I tried to show a confirm dialog when user choose a popup menu. But nothing happened when user click the menu, why?



2. Solution

@objc func settingsClicked(_ sender: UIButton) {
        let menuViewController = PopMenuViewController(sourceView:sender,actions: [
            PopMenuDefaultAction(title: "test1" , image: UIImage(named: "icon"),didSelect: { (PopMenuAction) in
                DispatchQueue.main.async {
                    UIUtils.shared.showConfirm(controller: self, message: "msg1") {
                        DispatchQueue.global().async {
                            MessageService.shared.updateMessageReadAll() {//update
                                self.loadMessages(queryLatest: false,failedHandlerx: nil) {//reload data to buffer
                                    DispatchQueue.main.async {
                                        self.tableView.reloadData() //reload tableview
                                    }
                                }
                            }
                        }
                    }
                }
            })
        ])
    }

The key point is that I wrap the show-dialog-code in a DispatchQueue.main.async thread like this:

DispatchQueue.main.async {
    UIUtils.shared.showConfirm(controller: self, message: "msg1") {
        DispatchQueue.global().async {
            MessageService.shared.updateMessageReadAll() {//update
                self.loadMessages(queryLatest: false,failedHandlerx: nil) {//reload data to buffer
                    DispatchQueue.main.async {
                        self.tableView.reloadData() //reload tableview
                    }
                }
            }
        }
    }
}

Now test it again:

now it works!



3. Why does it work?

So the key part is to wrap the confirm dialog code within a main async block

DispatchQueue.main.async {
    //show UI dialog here
}

What is DispatchQueue.main?

Dispatch queues are FIFO queues to which your application can submit tasks in the form of block objects. Dispatch queues execute tasks either serially or concurrently. DispatchQueue.main is an object that manages the execution of tasks serially or concurrently on your app's main thread or on a background thread. iOS 8.0+ iPadOS 8.0+ macOS 10.10+

Quoted from https://www.objc.io/issues/2-concurrency/concurrency-apis-and-pitfalls/:

The important thing to keep in mind is that you have no control over where and when your code gets scheduled, and when and for how long its execution will be paused in order for other tasks to take their turn. This kind of thread scheduling is a very powerful technique. However, it also comes with great complexity, which we will investigate later on.

When to use DispatchQueue.main?

The simplest answer is to always use it when you're updating UI in a delegate method or completion closure because you don't control how or when that code is called

In iOS development, the UI development framework we use, namely UIKit or SwiftUI, is not thread-safe: the processing of user input and the drawing of UI must be performed in the main runloop bound to the main thread.

4. Summary

In this post, I demonstrated how to solve the UI dialog now showing problem and solution, the key point is to wrap the UI code in a DispatchQueue.main thread. That’s it, thanks for your reading.