Dispatch.main queue questions since I might be confused

I am new to swift and have been working my first application for a while now. During testing I have seen some crashes in my code and I believe they are caused by some of the UI updates not being processed on the main thread. I googled around and I read all "UI Updates" need to be made on the main thread. Will someone please confirm what exactly a "UI update" actually is for me?


Do I need to wrap all of the following inside "DispatchQueue.main.async" ?


  • updating text in a TextView
  • updating a UIButton text
  • adding corner radius and button width values to a UIButton
  • everytime I want to present an alert message
  • each time I call "dismiss(animated: true, completion: nil)" to close the current ViewController and return to the previous one
  • each time I call "storyBoard.instantiateViewController(withIdentifier: )" to create a ViewController and also when calling "present( )"
  • updating the myLabel.frame.size values (width and height)
  • updating a scrollview.frame.size value
  • when the "isHidden" property of a button, label, etc is set
  • setting colors to labels, buttons, etc.


In my viewDidLoad( ), viewWillLayoutSubviews( ), viewDidLayoutSubviews( ), etc. functions I hide objects, set borders, set colors, set ScrollView sizes, etc.. so would I use "DispatchQueue.main.async" inside all of these functions?


I do a bunch of UI updates (I believe) so wanted to understand whether I need to add a ton of separate "DispatchQueue.main.async" wrappers all over my code. It seems I would need plenty and I am concerned about polluting my code, where all you see is a ton of "DispatchQueue.main.async" wrappers all over the place.


Does my application start off using the main thread and then for some reason goes into the background thread? if so, then perhaps I would only need to use Dispatch in cases where my application would switch to the background thread? I would not know when this would occur of course.


I am also concerned that if I use "async" throughout my code then the code inside the Dispatch region will not execute right then but rather continue on executing the code located after/outside the dispatch region, whcih would be out of order as I would like the code to execute.


Perhaps someone can confirm all of this for me?



ASIDE

I tried using a DispatchQueue.main.async code wrapper around some code which updates label and button text. I confirmed the code inside the Dispatch region occasionally does not execute. I see the print statements which are before and after the Dispatch region but sometimes(occasionally) the print statements inside the region do not get hit. Should I be using "sync" instead of "async" to guarantee by code always runs when I want it to?

Accepted Reply

It is possible. But you can't just blindly "wrap" code in an async dispatch. Those are "asynchronous". You aren't "wrapping" the code. That is just an artifact of how you are expressing behaviour in the language. What you are doing is appending a block of code to a queue to be executed "later". It could be the same thread or a different thread. It could execute right away or after a few milliseconds. The code that follows it cannot assume that the dispatched block either has or has not run. You can do a sync dispatch, but that can be even trickier.


Performing actions in a delegate callback can also be tricky. That may be a case where dispatching onto the main thread is the correct solutions. I don't know. Sometimes it does work that way.

Replies

This usually doesn't cause a crash. It spits out an error message to console and locks up your app for a bit in debug.


But unless you are actively creating concurrent queues and putting work on them, this isn't something you need to think about. Everything (more or less) runs on the main thread. If you are creating concurrent queues and putting work on them, then you need to be careful about what you are doing there. Don't just blindly start wrapping everything in dispatches.

Thanks for the input.


I am not an advanced user and do not user concurrent queues (at least not intentionally) at all. I am just learning about Dispatch queues now due to some unexpectedly crashes I am experiencing in my application.


Throughout my testing I have had Xcode's "Main Thread Checker" indicate a couple areas in my code where I needed to wrap around a Dispatch.main to ensure that code is running on the main thread so I am using Dispatch in these areas.


One more thing to share. I created a game which uses the GameKitHelper class to interface into Game Center. I set up a delegate to this GameKitHelper class to receive callback function calls when certain events occur. When a certain delegate function is called, my code will create and then present a new ViewController using this code:


let storyBoard = UIStoryboard(name: "Main", bundle: nil)
if let myVC: myViewController = storyBoard.instantiateViewController(withIdentifier: "myViewController") as? myViewController
{
  present(myVC, animated: true, completion: nil)
}


During testing, my application occasionally experiences a crash at the point the new ViewController should be displayed. Since I am inside a delegate callback function, which deals with UIStoryboard and creating a new ViewController to be presented (ie: displayed in the UI), then I am thinking I might need to have the code (above) wrapped in DispatchQueue.main.async to avoid the crash.


Would you expect an application to crash in the case described above when no Dispatch is being used ?


I print out the current thread name using "Thread.current.threadName". I do see most of the time the thread is the Main Thread, however, I did see one time a thread name was called "com.apple.root.default-qos" so I know it is not always the Main Thread. If the thread is not the Main Thread (for some reason) and the code above (with no Dispatch) is called then would a crash occur?

It is possible. But you can't just blindly "wrap" code in an async dispatch. Those are "asynchronous". You aren't "wrapping" the code. That is just an artifact of how you are expressing behaviour in the language. What you are doing is appending a block of code to a queue to be executed "later". It could be the same thread or a different thread. It could execute right away or after a few milliseconds. The code that follows it cannot assume that the dispatched block either has or has not run. You can do a sync dispatch, but that can be even trickier.


Performing actions in a delegate callback can also be tricky. That may be a case where dispatching onto the main thread is the correct solutions. I don't know. Sometimes it does work that way.

I updated all my delegate callback functions to ensure any UI updates are made on the main thread by placing the UI code logic in DispatchQueue.main.async. With my debug logging I can see the UI code is now being executed after the current function (and any parent calling functions) have completed. So far, it is ok for my UI logic to take place at that point so I might be good. I will start testing and ccross my fingers that my crashes go away so I can finally submit my application for review.


Thanks again John for your time.

My crashes have disappeared ! Thanks for all the help !

Yep sure do