Search code examples
iosswiftclosuresuialertcontroller

UIAlertAction handler/closure does not execute


I have an UIViewController extension which I am using to display alerts in any view controller. It worked fine until this weird use case happened:

extension UIViewController {
    func showModal(title: String, msg: String, handler: ((UIAlertAction) -> Void)? = nil) {
        let alert = UIAlertController(title: title, message: msg, preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: handler))
        DispatchQueue.main.async {
            self.present(alert, animated: true, completion: nil)
        }
    }
}

Inside a view controller I tap a button which triggers a network fetch:

@IBAction func didTapSave(_ sender: UIButton) {

Task {
    
    let result = await BalanceManager.shared.fetchAllBalances()
    
    switch result {
    case .success(_):
        self.showModal(title: "", msg: "account successfully saved") { (_) in
            //This code always gets executed
            self.navigationController?.popViewController(animated: true)
        }
    case .failure(let failure):
        self.showModal(title: "", msg: "failure") { (_) in
            //Code in here is not executed
            print("Failure closure")
        }
    }
}

I don't understand why on case ".failure" the closure for showModal does not execute. If I set a breakpoint on the line with self.showModal the code gets there but does not execute the closure when I tap "OK" on the popup


Solution

  • I figured it out, it was my programming mistake and lack of proper sleep :). Another VC which was listening for notifications about the result of the same fetch was triggering a popup even though the VC was not visible, but was alive, embedded in a tab bar controller. So my "failure" popup was never executed properly. I figured it out after I updated the showModal method with the sender parameter and following that I have added a fix that requires the calling VC to be visible when it wants to present a popup:

    func showModal(sender: UIViewController, title: String, msg: String, handler: ((UIAlertAction) -> Void)? = nil) {
        let alert = UIAlertController(title: title, message: msg, preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: handler))
        DispatchQueue.main.async {
          if sender.isVisible() {
            self.present(alert, animated: true, completion: nil)
          }
        }
      }
    

    where isVisible():

    extension UIViewController {
      func isVisible() -> Bool {
        return self.isViewLoaded && self.view.window != nil
      }
    }