Do I need dispatch_get_main_queue in completion block - ios

In one of the tutorial from Ray Wenderlich Series, he used dispatch_get_main_queue() inside the completion block as follows
func startFiltrationForRecord(photoDetails: PhotoRecord, indexPath: NSIndexPath){
if let filterOperation = pendingOperations.filtrationsInProgress[indexPath]{
return
}
let filterer = ImageFiltration(photoRecord: photoDetails)
filterer.completionBlock = {
if filterer.cancelled {
return
}
dispatch_async(dispatch_get_main_queue(), {
self.pendingOperations.filtrationsInProgress.removeValueForKey(indexPath)
self.tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
})
}
pendingOperations.filtrationsInProgress[indexPath] = filterer
pendingOperations.filtrationQueue.addOperation(filterer)
}
Even though he briefly explained why the completion block is required, I was wondering if anyone could answer the following questions
In my own app, I have completion block in quite a lot of place (with reloading table view code in completion block like his. However, I don't not have a single dispatch_get_main_queue code. Does that mean for all UI related task in completion block, I NEED to add dispatch_get_main_queue?

Yes , you have to use main queue to update tableview. As any UI update should be perform on main thread.
So you must have to reload table on main thread.
dispatch_async(dispatch_get_main_queue(), ^{
// Perform UI operations here
});
It is advisable to perform all calculations, network related functions on secondary thread or background thread, when it comes to perform operation related to UIKit, then simple switch back to main thread using above mention code.

Related

Swift: Update UI - Entire function on main thread or just the UI update?

I've read that the UI should always be updated on the main thread. However, I'm a little confused when it comes to the preferred method to implement these updates.
I have various functions that perform some conditional checks then the result is used to determine how to update the UI. My question is should the entire function run on the main thread? Should just the UI update? Can / should I run the conditional checks on another thread? Does it depend on what the function does or how fast you want it done?
Example a function that changes the image inside an ImageView without threading:
#IBAction func undoPressed(_ sender: Any) {
if !previousDrawings.isEmpty {
previousDrawings.remove(at: previousDrawings.count - 1)
if let lastDrawing = previousDrawings.last {
topImageView.image = lastDrawing
}
else {
// empty
topImageView.image = nil
}
}
}
Should I be setting topImageView.image on the main thread? Like this:
#IBAction func undoPressed(_ sender: Any) {
if !previousDrawings.isEmpty {
previousDrawings.remove(at: previousDrawings.count - 1)
if let lastDrawing = previousDrawings.last {
DispatchQueue.main.async {
self.topImageView.image = lastDrawing
}
}
else {
DispatchQueue.main.async {
self.topImageView.image = nil
}
}
}
}
Should I be using a background thread for the conditional checks? Like this:
#IBAction func undoPressed(_ sender: Any) {
DispatchQueue.global(qos: .utility).async {
if !previousDrawings.isEmpty {
previousDrawings.remove(at: previousDrawings.count - 1)
if let lastDrawing = previousDrawings.last {
DispatchQueue.main.async {
self.topImageView.image = lastDrawing
}
}
else {
DispatchQueue.main.async {
self.topImageView.image = nil
}
}
}
}
}
If someone could explain what method is preferred and why that would be really helpful.
Back up. Except in special circumstances, all your code is run on the main thread. UIAction methods, for example, are ALWAYS executed on the main thread, as are all the methods defined by UIViewController and it's various subclasses. In fact, you can safely say that UIKit methods are performed on the main thread. Again, your methods will only be called on a background thread in very special circumstances, which are well documented.
You can use GCD to run blocks of code on background threads. In that case, the code is being run on a background thread because you explicitly asked for that to happen.
Some system functions (like URLSession) call their delegate methods/run their completion handlers on background threads by default. Those are well documented. For third party libraries like AlamoFire or FireBase, you'll have to read the documentation, but any code that's called on a background thread should be very well documented because you have to take special precautions for code that runs on a background thread.
The usual reason to use a background thread is so that a long-running task can run to completion without freezing the user interface until it's done.
A common pattern for, example, is using URLSession to read some JSON data from a remote server. The completion handler is called on a background thread since it might take time to parse the data you get back. Once you are done parsing it, though, you'd wrap a call to update the UI in a GCD call to the main thread, since UI changes must be performed on the main thread.
First off, your undoPressed method will be called on the main queue.
In the first set of code, everything will be on the main queue.
In the second set of code, using DispatchQueue.main.async is pointless since the rest of the code is already on the main queue.
So really your only two sensible options are 1 and 3.
Given your code, option 1 is fine. You would only want to use option 3 if the code being run in the background took more than a trivial amount of time to execute. Since the code you have here is trivial and will take virtually no time to execute, there is no point in option 3 here.
So simply use your first set of code and you'll be fine.
Worry about moving code to the background when it need to perform a big loop or calculate a complicated algorithm or perform any sort of network access.
To make it simple, make the calculation and then everything related to that updated calculation that needs to be reflected in the UI should be done from:
DispatchQueue.main.async{ //code }
that is using main thread.

Why my cells do not load at the same time?

I'm using background thread for getting my data from the network and main thread for presenting my cells:
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), {
let _ = self.http.getInfo() { (result) in
self.cellsArray = result
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
})
completion(completed: true)
}
})
where self.http.getInfo is a NSURLSession.sharedSession().dataTaskWithRequest method for taking my network infos.
And when I run my app my cells start showing separately as: the first appears, in a second the next one appears, in 2 seconds the next one and etc.
Why they do not appear at the same time? And also, because of the background and main thread using, my UITableView jumps when I implement infinite scroll and append new cells to exist cell array.
How can I fix it?
write self.tableView.reloadData() this statement in completion handler of NSURLSession.sharedSession().dataTaskWithRequest.

Performing a Segue Inside of a Completion Handler

I'm attempting to perform a storyboard segue inside of a completion handler like so:
movieWriter.finishRecordingWithCompletionHandler({ () -> Void in
//Leave this view
self.performSegueWithIdentifier("decisionSegue", sender: self)
})
and getting the following warning:
This application is modifying the autolayout engine from a background thread, which can lead to engine corruption and weird crashes. This will cause an exception in a future release.
The completion handler is running on a background so I understand why I'm getting this error, my question is what are my options for performing this segue without getting this error?
The reason I'm performing the segue in the completion handler is that the completion handler is called after a recorded movie is done being written to file and the view being segued to plays the movie, hence it needs to be on file before segueing.
Whenever your perform any operation on UI/active view then it has to be on main thread and not background thread.
Do as following:
__weak typeof(self) weakSelf = self; //Best practice
//Provide a weak reference in block and not strong.
movieWriter.finishRecordingWithCompletionHandler({ () -> Void in
dispatch_async(dispatch_get_main_queue(),{
weakSelf.performSegueWithIdentifier("decisionSegue", sender:weakSelf)
})
})
Put it in dispatch queue :
dispatch_async(dispatch_get_main_queue(),{
self.performSegueWithIdentifier("decisionSegue", sender: self)
})
Hope it will work
For more detailed information : This application is modifying the autolayout engine from a background thread, which can lead to engine corruption and weird crashes
This Error is telling that You are performing some UI update task from Background Thread and it's not possible to Update UI from background thread so you have to access main Thread and then perform segue.
Objective-C:
dispatch_async(dispatch_get_main_queue(), ^{
// update some UI
// Perform your Segue here
});
Swift:
DispatchQueue.main.async {
// update some UI
// Perform your Segue here
}
Hope it will help you.

chaining UITableview Updates on main thread

So what i am attempting to do is conceptually very simple however I have not been able to find a solution for it:
I am trying to remove cells from a tableView animated with the:
self.coolTableView?.deleteRowsAtIndexPaths
function, to do this I change the dataSet and perform this action, right after it is done i would like to change the data set again and use:
self.coolTableView?.insertRowsAtIndexPaths
to reflect and animate the second change to the dataset.
The Problem I run into is that if I use:
dispatch_async(dispatch_get_main_queue()) { () -> Void in
//Update tableview
}
they seem to lock each other out, the used memory just keeps skyrocketing and it hangs. I am assuming they are interfering with each other. now my next thought was to change the code to sync so:
dispatch_sync(dispatch_get_main_queue()) { () -> Void in
//Update tableview
}
however the first update hangs and and ceases operation. With the research I have done it sounds like I am putting the execution of the block in the main queue behind my current running application and vuwala, that is why it hangs.
what is the correct way to block execution until I can complete an animation on the main thread so i do not blow up my data source before animations can take place?
The animations in iOS take a block that they can execute when the animation terminates. Try putting operations on coolTableView into a closure (remember about unowned self to not create memory leaks) and pass it as completion closure to your animation.
example:
let someAnimations: () -> Void = {
//some animations
}
let manipulateTableView: (Bool) -> Void = {
isAnimationFinished in
// perform some data manipulation
}
UIView.animateWithDuration(0.5, animations: someAnimations, completion: manipulateTableView)

dispatch_async when initializing UIView objects

so here is the problem:
I've got a few heavy views with many subviews, which I need to load and then to display. I want to do it asynchronously so that I don't block my main thread. When I tried to do it asynchronously I encountered the following dilemma:
After all of the heavy lifting job has been done and I return to the main queue to actually display that stuff, I get problems. First of all even though everything is done it takes 30-60 seconds for all the views to become visible. Sometimes they get misplaced. What could I be doing wrong and what should I be looking for ?
private func loadScrollViews() {
let qos = Int(QOS_CLASS_USER_INTERACTIVE.value)
dispatch_async(dispatch_get_global_queue(qos, 0)) { () -> Void in
// Creating many UIViews
for var i = 0; i < 100; i++ {
let view = UIView(frame: someFrame)
self.viewCollection.append(view)
}
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.displayViews()
})
}
}
private func displayViews() {
for view in self.viewCollection {
self.contentView.addSubview(view)
}
self.activityIndicator.stopAnimating()
self.contentView.hidden = false
}
After displayViews gets executed as I said views take almost a minute to appear on the screen.
UIView manipulation should be done on main thread
from doc
Threading Considerations
Manipulations to your application’s user interface must occur on the
main thread. Thus, you should always call the methods of the UIView
class from code running in the main thread of your application. The
only time this may not be strictly necessary is when creating the view
object itself but all other manipulations should occur on the main
thread.
if you have to create many UIView object then do it like this
dispatch_async() but use main_queue for this purpose. And one more thing if you want use background thread then think about using CALayer we can do most of CALayer work on background thread

Resources