Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 474
DoesData Avatar asked Jan 04 '18 19:01

DoesData


2 Answers

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.

like image 73
Duncan C Avatar answered Oct 31 '22 15:10

Duncan C


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.

like image 23
rmaddy Avatar answered Oct 31 '22 16:10

rmaddy