Problem with sleep() code

Code Block
  override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
     
    for percent in 1...10 {
       
      print(percent)
      self.accuracyLabel.text = "\(percent)" + "%"
      sleep(UInt32(0.9))
    }
  }
   @IBOutlet weak var accuracyLabel: UILabel!


Hi,
I have 2 problems with this code:

1) sleep(UInt32(1.0)) correctly prints 'percent' every 1 second.
But as soon as I go under 1.0, such as sleep(UInt32(0.9)), the print don't happen every 0.9 seconds, but instantly. Why is that?

2)    @IBOutlet weak var accuracyLabel: UILabel! text is not updated on each iteration, but only after entire loop has completed. This is not what I would have expected, I would have expected update on each iteration, why is it not the case?

regards,
Brak


Answered by Claude31 in 671380022

Why is that?

Because sleep uses seconds.
And 0.9 is rounded to zero.

Use asleep instead, with number in micro seconds

Code Block
usleep(900000)

UILabel! text is not updated on each iteration, but only after entire loop has completed.

You need to execute in another thread and then update in the main thread with a dispatchQueue

So correct code would be:

Code Block
@IBOutlet weak var accuracyLabel: UILabel! // I find it better to declare at beginning of class
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
DispatchQueue.global().async { // Dispatch to another thread
for percent in 1...10 {
print(percent)
DispatchQueue.main.async { // to update UI, go back to main thread
self.accuracyLabel.text = "\(percent)" + "%"
}
usleep(900000) // do it in other thread, never on main thread
}
}


Tell if you have other problem. Otherwise, don't forget to close the thread. Good continuation.
Accepted Answer

Why is that?

Because sleep uses seconds.
And 0.9 is rounded to zero.

Use asleep instead, with number in micro seconds

Code Block
usleep(900000)

UILabel! text is not updated on each iteration, but only after entire loop has completed.

You need to execute in another thread and then update in the main thread with a dispatchQueue

So correct code would be:

Code Block
@IBOutlet weak var accuracyLabel: UILabel! // I find it better to declare at beginning of class
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
DispatchQueue.global().async { // Dispatch to another thread
for percent in 1...10 {
print(percent)
DispatchQueue.main.async { // to update UI, go back to main thread
self.accuracyLabel.text = "\(percent)" + "%"
}
usleep(900000) // do it in other thread, never on main thread
}
}


Tell if you have other problem. Otherwise, don't forget to close the thread. Good continuation.
Thanks Claude31, great answer.
Thanks for the feedback.

A little more explanation.

The problem you had of UI not updating is a "grand classic" with iOS.

What happens (in simple words) when you do it directly, the updates of UI are "stacked" and executed at the end of the loop. So you see no progress.

When you dispatch to another thread :
  • on each iteration, you update value

  • then you return to the main thread which will execute your order

  • you wait in the secondary thread and continue.

Problem with sleep() code
 
 
Q