简体   繁体   中英

display UIAlert will crash the app on present?

I have a test connection action logic. it will test the server in live or not. the bug happens when the logic finishes the test and try display UIAlertController inside closure thread it will crash the systems.

@IBAction func TestNetwork(_ sender: Any) {
    var message = "\(internetConnection) internet connection \n \(serverStatus) server\n  "
    self.showSpinner(onView: self.view)
    DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
        self.testConnection(connectionTestCompletionHanlder: {connectionResult in


            let alertController = UIAlertController(title: "Alert", message: message, preferredStyle: .alert)
            let defaultAction = UIAlertAction(title: "OK", style: .default) { (action) in
                // Respond to user selection of the action.
            }
            alertController.addAction(defaultAction)
            //self.removeSpinner()
            self.present(alertController, animated: true){
                // The alert was presented
            }
        })
    }
}

error

-[Assert] Cannot be called with asCopy = NO on non-main thread. +[UIView setAnimationsEnabled:] being called from a background thread. Performing any operation from a background thread on UIView or a subclass is not supported and may result in unexpected and insidious behavior.
-Unsupported use of UIKit view-customization API off the main thread. -setHasDimmingView: sent to <_UIAlertControllerView: 0x7ffe2ff8b4a0; frame = (0 0; 375 667); layer = >

-Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Modifications to the layout engine must not be performed from a background thread after it has been accessed from the main thread.'

As per the comments above, you should always launch UI work on the main thread. The completion handler you have above, although instantiated on the main thread through the asyncAfter could execute on any thread, so you therefore need to change the section that opens the alert controller to explicitly be on the main thread:

//previous code as above...

DispatchQueue.main.async {
  self.present(alertController, animated: true){
    // The alert was present
  }
}

You could also set the alert controller's completion handler to nil if your are not planning to utilise it, rather than having an empty closure.

I had a similar issue. Answering here if someone else encounters it.

The app can also crash in iOS 13.3 (at least) when showing the alert to ask for permissions, when your app uses SceneDelegate even with the Application Scene Manifest entries are present in plist file. Had to remove the SceneDelegate and the related entries from the info.plist file, and no more crash.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM