1

I have a function that can run for a while. While it is running, I want to provide feedback on the screen.

The problem is that the label.text is not updating.

I suspect this is because I need to execute the function in the background, otherwise execution "halts" in the viewDidAppear method. But if I do that, I think updates in the background do not update the text as well. So I think I must update the label.text on the main thread again.

But I get a runtime error when I do this:

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var label: UILabel!

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        DispatchQueue.main.async {
            for i in 1...10 {
                DispatchQueue.main.sync {
                    self.label.text = "Working on item \(i)..."
                }
                sleep(1)
            }
        }
    }
}

I had a look at this post, but the methods appear to be dated and do not work in Xcode 10 / Swift 4.

rmaddy
  • 314,917
  • 42
  • 532
  • 579

2 Answers2

2

I did some research on background queues and found this post.

Using that in my example - this works:

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var label: UILabel!

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        DispatchQueue.global(qos: .background).async {
            for i in 1...10 {
                DispatchQueue.main.async {
                    self.label.text = "Working on item \(i)..."
                }
                sleep(1)
            }
        }
    }
}
2

The problem is that you run the for loop in the main thread. which means that one rendering frame will execute the entire for loop.

You should run long tasks ONLY on a background thread and update necessary UI elements in the main thread:

  DispatchQueue.global(qos: .background).async {// global() will use a background thread rather than the main thread
            for i in 1...10 {
                DispatchQueue.main.sync {
                    self.label.text = "Working on item \(i)..."
                }
                sleep(1)
            }
        }
giorashc
  • 13,691
  • 3
  • 35
  • 71