2

I have an IBAction that calls a function that sometimes displays error alerts from an asynchronous thread. It "works" in the simulator, but I got this error:

CoreAnimation: warning, deleted thread with uncommitted CATransaction; set CA_DEBUG_TRANSACTIONS=1 in environment to log backtraces.

Upon seeing that error, I realized I was trying to update the UI off the main thread and I need to fix it.

Here's the asynchronous function I'm calling:

let queue = DispatchQueue(label: "com.adrianbindc.myApp.myFunction")

queue.async {
    self.createArray()

    switch self.arrayIsEmpty() {
    case true:
        // TODO: update on the main thread
        self.displayAlert(alertTitle: "Error Title", alertMessage: "Error Message")
    case false:

        // Do Stuff Off the Main Queue
        DispatchQueue.main.async{
            switch someArray.count {
            case 0:
                // TODO: update on the main thread
                self.displayAlert(alertTitle: "Another Error Title", alertMessage: "Another error message.")
            default:
                // Do other stuff
            }
        }       
    }
}

/**
 Utility method to display a single alert with an OK button.
 */
func displayAlert(alertTitle: String, alertMessage: String) {
    let alertController = UIAlertController(title: alertTitle, message: alertMessage, preferredStyle: .alert)
    let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
    alertController.addAction(okAction)
    self.present(alertController, animated: false, completion: nil)
}

What I've tried

I tried adding @objc in front of my displayAlert function and calling the alert function like this within the async block:

self.performSelector(onMainThread: #selector(ViewController.displayAlert(alertTitle: "Error Title", alertMessage: "Error Message")), with: self, waitUntilDone: false)

but I get this error in the compiler:

Use of instance member 'displayAlert' on type 'ViewController'; did you mean to use a value of type 'ViewController' instead?

Any suggestions re: where my mistake lives are greatly appreciated. Thank you for reading.

Adrian
  • 16,233
  • 18
  • 112
  • 180
  • I would use `DispatchQueue.main.async { displayAlert(alertTitle: "Error Title", alertMessage: "Error Message") }` instead of `performSelector` – Aaron Brager Jul 11 '16 at 02:44
  • 1
    @AaronBrager Thank you. That's what I ended up doing. I just posted my answer below :) – Adrian Jul 11 '16 at 02:49

1 Answers1

6

I was on the right path, but I was using incorrect syntax (per usual). When I call the alert function, that does stuff on the main thread which means I need to get the main thread.

Here's how I called alerts within the asynchronous block:

// ** This gets the main queue **
DispatchQueue.main.async(execute: {
    self.displayAlert(alertTitle: "Another Error Title", alertMessage: "Another error message.")
}

Here's what the finished product looks like:

let queue = DispatchQueue(label: "com.adrianbindc.myApp.myFunction")

queue.async {
    self.createArray()

    switch self.arrayIsEmpty() {
    case true:
        // ** This gets the main queue **
        DispatchQueue.main.async(execute: {
            self.displayAlert(alertTitle: "Error Title", alertMessage: "error message.")
        })

    case false:
        switch resultArray.count {
        case 0:
            // ** This gets the main queue **
            DispatchQueue.main.async(execute: {
                self.displayAlert(alertTitle: "Another Error Title", alertMessage: "Another error message.")
            })
        default:
            // Do other stuff
        }
    }
}

This answer got me over the finish line and has more scenarios which some may find helpful for Swift 3.0.

Community
  • 1
  • 1
Adrian
  • 16,233
  • 18
  • 112
  • 180