How can I use sleep in while without freezing all app? - swift

I have a while loop which looping until some expression get true for breaking the loop, inside this loop I got a sleep(1), everything works fine, the only problem is the app get frozen until end of the while loop which is logical, I am trying to find out a way that the app would not get frozen while "while" working! Maybe multithreading programming? is it possible?
var repeatLoop = true
var count: Int = 0
while repeatLoop {
print(count)
count += 1
sleep(1)
if count >= 10
{
repeatLoop = false
}
}
print("finished!")
PS: My Goal is finding the Answer for this question rather than changing the way of solving, I mean there is so many way to get the same result without sleep or while.

With this program you can't! You need to look for options in your programming framework which can do multi threading. Your sleep() function must allow other threads to run.
One nice solution is to invert the logic and use Timers. instead of call sleep to block the operation till the next interaction. Your timer will call your routine many times you want.
Look this: The ultimate guide to Timer

The main thread / queue is the interface thread / queue, so time-consuming activity there freezes the interface.
Maybe multithreading programming
Indeed. The answer to your actual question is: do the time-consuming activity on a background thread / queue.
DispatchQueue.global(qos: .background).async {
// your code here
}
Of course, this raises the spectre of concurrency-related issues involving multiple simultaneous execution of the same code, non-thread-safe conflicts between accesses to shared data, etc. But that is the price of doing time-consuming things, and you just have to learn to cope.

The app won't freeze if you don't run this code on the main/UI thread.
There are several ways to do this on iOS. Here's one quick way:
DispatchQueue.global(qos: .background).async
{
// Your code here.
}

Related

Is it ok to use DispatchQueue inside Task?

I'm now converting some of my codes into concurrency codes with async-await and Task. One thing I wonder is it ok to use DispatchQueue inside Task instances like
Task {
await someHeavyStuff()
DispatchQueue.main.async {
someUIThreadStuff()
}
}
As I know Task and DispatchQueue has little different mechanism to handle asynchronous things, so I am worried that using both could mess up the threading system.
(I know that I can use MainActor.run {} in this case)
You get away with DispatchQueue.main.async { … }, but you really should just retire this pattern. But if you have a big complicated project that you are slowly transitioning to Swift concurrency, and do not have the time and clean this up quite yet, yes, you can get away with this GCD call for now.
But the right solution is to just mark someUIThreadStuff as #MainActor and retire the DispatchQueue.main.async { … }. It is such a trivial fix, as is MainActor.run { … }. Of everything you might be tackling in the transition to Swift concurrency, this is one of the easiest things to just do right, and get rid of GCD API.
Where you have to be especially careful as you transition to Swift concurrency is where you use locks and semaphores or where you block the current thread. Swift concurrency can not reason about these, and those can be sources of problems. But a lingering dispatch to the main queue is unlikely to cause problems, though you should certainly excise it at your earliest convenience. See Swift concurrency: Behind the scenes, particularly the discussions about the runtime contract to never prevent forward progress.
As I look at your code snippet, I would be more concerned about the Task { … } to start someHeavyStuff. The name “startHeavyStuff” suggests something that is computationally expensive, blocking the current thread. But Task { … } is for launching asynchronous tasks on the current actor, not for running “heavy” tasks on a background thread. Now, someHeavyStuff is getting it off the current actor somehow, then ignore this caveat. But be careful you do not assume that Task { … } will behave like DispatchQueue.global().async { … }, because it does not.
I would suggest watching WWDC 2021 Swift concurrency: Update a sample app. It walks through a very practical exercise of refactoring your legacy code.

Swift UIAnimation blocked by for loop

I have a UIAnimation that pulls out a message log, but I need to populate the message log using a for loop as well. I have the animation taking place before the for loop begins, however, the animation doesn't actually begin until after the for loop finishes executing (even though the loop is located after the UIAnimation).
I've tried putting the contents of the for loop within an autoreleasepool (did not work)
I've tried running the for loop on a background thread, however, most of the code within the loop needs to be executed on the main thread, and I haven't had success with that approach.
The for loop is very large, and most of the code is irrelevant to the question, so you can set this up using any UIAnimation, and any relatively time consuming for loop...
for example:
let someView = UIView(frame:CGRect(x:self.view.frame.size.width,y:0,width:self.view.frame.size.width,height:self.view.frame.size.height))
someView.backgroundColor = .red
UIView.animate(withDuration:0.3,animations:{
self.someView.frame.origin.x = 0
})
//You can wrap this with something like:
//DispatchQueue.global(qos: .background).async(execute: {
//But this^ doesn't work on the majority of the code within the loop
for i in 0...1000{
print("hopefully this loop takes a few seconds to finish executing")
print("mainthread tasks being executed")
print("populating UITable in my particular case, and rendering Images/Video/text")
}
The closest that I can get to making it better is to add a "completion" to my UIAnimation, (which will allow the UIView to pull out completely/immediately) but then there is still a second of wait time before the first messages appear...
Also, I am populating a UITableView within the for loop. Not sure if that has any extra relevance to the primary issue I'm having or not...
I'm pretty familiar with DispatchQueue and the differences between async/sync. If you have any potential solutions, or ideas, please let me know.
The animation does not happen immediately, when you call UIView.animate. In stead all the animations are collected and run at some appropriate time. This is why the animation only starts after your for loop is done.
So the way to fix it is to get the animation to run before your for loop runs. Now, accepting what you say, that the for must run on the main thread, you have a number of options. You could put the for loop inside a DispatchQueue.main.async{} or somehow throttle it down so that the animation gets going first. Your idea of using the completion is a good one but you mention some practical issues you picked up with it. So either solve those practical issues, try to run the for inside a DispatchQueue.main.async{} or even, based on practical considerations, start the for loop on the main thread but somewhere else, say in something like didLayoutSubviews (fixed) or as now together with the animation but after a few millis delay to allow the animation to get going.
It's an interesting question. This made me think!
Yes, you mentioned that you understand differences between async/sync but didn't mention if you tried animating in the main thread.
Did you try putting the
UIView.animate(...) in DispatchQueue.main.async{}?

Why does the order of a dispatched print() change when run in a Playground?

In a Udacity's GCD course there was a small quiz:
let q = DispatchQueue.global(qos: .userInteractive)
q.async { () -> Void in
print("tic")
}
print("tac")
Which will be printed first?
The correct answer is: tac, then tic. Seems logical.
But, why is it so only when I create an Xcode project? In a playground it prints tic then tac. What am I missing?
In GCD
DispatchQueue.global(qos: .userInteractive).async{}
is below
DispatchQueue.main.async{}
Even thought it has quality of service (qos) as UI thread it does not means it is a main thread. So may be there is a difference in performance with play ground and ui project
please check apples documentation as well.
Apples documentation on GCD
key to your answer is in the question what really are you asking the "system" to do, an by system that is the whatever the code is running on the playground, your computer/phone or emulator. You are executing an asynchronous "block" of code - print("tic") with some priority ".userInteractive". If the system handles the asynchronous block first or continues with "normal" execution depends on priority and available resources. With asynchronous calls there is no real why to guarantee that it is executed before or after the code continues that is the nature of being asynchronous i.e execute the block as soon as the system allows it, all without blocking your current work. So the difference you are seeing in playground vs a project/emulator is that the project/phone/emulator must keep the UI/primary thread responsive so it continue with print("tac"), while the development playground favors the thread executing print("tic"). Bottom line is it has to deal with priority of execution and the available resources and how its implemented on the system you're running the code.

swift stop loop and continue it with a button

is it possible to stop an for in loop and continue it and the stopped position with a button.
i mean something like this:
for x in 0..<5 {
if x == 3 {
// show a button
// this button have to be pressed
// which do some tasks
// when all tasks are finished >
// continue this loop at the stopped point
}
}
is this possible? if yes, how?
Short answer: use a semaphore
Longer answer:
Your situation is an example of the more general case of how to pause some computation, saving its current state (local variables, call stack, etc.), and later to resume it from the same point, with all state restored.
Some languages/systems provide coroutines to support this, others the more esoteric call with current continuation, neither are available to you (currently) Swift...
What you do have is Grand Central Dispatch (GCD), provided in Swift through Dispatch, which provides support for executing concurrent asynchronous tasks and synchronisation between them. Other concurrency mechanisms such as pthread are also available, but GCD tends to be recommended.
Using GCD an outline of a solution is:
Execute you loop as an asynchronous concurrent task. It must not be executing on the main thread or you will deadlock...
When you wish to pause:
Create a semaphore
Start another async task to display the button, run the other jobs etc. This new task must signal the semaphore when it is finished. The new task may call the main thread to perform UI operations.
Your loop task waits on the semaphore, this will block the loop task until the button task signals it.
This may sound complicated but with Swift block syntax and Dispatch it is quite simple. However you do need to read up on GCD first!
Alternatively you can ask whether you can restructure your solution into multiple parts so saving/restoring the current state is not required. You might find designs such as continuation passing style useful, which again is quite easy using Swift's blocks.
HTH

Is there an advantage on the iPhone to breaking up an operation into multiple threads?

I have a method in my iPhone app that is very repetitive (called over and over and over sequentially). The method is quite bulky and takes about one second to complete.
My question is this: If I was to muti-thread it to run, say 5 method calls on different threads simultaneously, would that be any faster than running 5 calls one after another? I know of desktop machines with multi-core processors this would be an advantage, but I am not sure about the iPhone.
Any advice?
Use Grand Central Dispatch (aka GCD and libdispatch) and it'll just Do The Right Thing(tm).
If you call dispatch_async on one of the global queues, GCD will figure depending on system workload whether to spawn new threads to handle the operations you submit, or whether to run them in series on one thread.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Do your calculations here
// If you want to run code on the main loop with the result of the calculations,
// do something like this
dispatch_async(dispatch_get_main_queue(), ^{
// Code to run on the main thread here
});
});
You could also use dispatch groups to run some code after all five calls have completed.
Take a look at the Concurrency Programming Guide and the GCD reference manual for details.
Update
Also look at this article from Ars on GCD. They explain some of the details of how GCD decides how to schedule blocks.
If it's something like a network request, it will most probably be faster because you're spending time waiting without really using the processor (although spawning threads is not really the best way to deal with that). But if the method is doing some heavy calculations you won't gain anything. Still, to keep the UI responsive you would run lengthy tasks in the background. It's usually better and simpler to use NSOperation for such things, so have a look at that class.
Try it, there is no better answer. It’s not hard:
- (void) startInParallel {
for (int i=0; i<5; i++)
[self performSelectorInBackground:#selector(doSomeWork)];
}
Plus you might want to add some time measuring. Then, if you find out that running all the operations in parallel really makes sense, you can use NSOperations or GCD to do it. (I just hope that performSelectorInBackground does not have enough time cost to skew the test results.)