Display a progressHud before the network call in swift - ios

In my code I am executing a network call which takes few seconds to complete. While it is executing I want to display a progresshud in the background. But the problem is the progresshud does not appears before the network call. It appears right after the network call finishes. I can not understand the issue.
My code is below.
func draw() {
if !self.drawing) {
self.progressHud.show(in: self.view)
self.drawing = true
self.drawImage() // this is the function that takes time to execute
}
else if (self.isTransformViewEnabled){
self.drawing = false
}
}
Please help

I think you are calling the network call synchronously. So it waits the request to complete for showing hud. You can call in on Main thread on async
DispatchQueue.main.async {
//your code here
}

This is happening because you are making the network call on the main queue synchronously. Hence the UI is updated after the call is completed.
Please make the network call asynchronously in a different queue and the HUD will show up on the screen.
DispatchQueue.main.async {
self.drawImage()
}
Let me know if it works for you.
Happy to help.
Thanks.

Related

How to pause operation queue in Swift

I have a couple of operations to perform on the IoT device from iOS App. So All my operations are in OperationsQueue with serial operations.
Here I want to perform one operation at a time and each operation needs to wait until I get a response from the IoT device.
Here IoT device response will take time to send back. so how to wait for the current operation in operation queue until I get a response from IoT.
So is there any way to pause current running operation until getting a response from IoT and then I will resume it so that the next operation in operation queue will start.
I tried with Sleep operation But it required time, but we can not guarantee about IoT device response.
Any suggestions would appreciate it. Thank you in advance.
The basic idea is that you don’t pause (or wait, or sleep), but rather you define a “concurrent” operation (see discussion of concurrent operations in the documentation) that doesn’t trigger the isFinished KVO until the device responds.
A simple way to do this is to write a concurrent operation class, like the one shown in this answer. Then your IoT operation can subclass that AsynchronousOperation class, and just call finish() when the device responds.
Then your operation queue (which presumably has a maxConcurrentOperationCount of 1, or perhaps is using dependencies), will not start an operation until the prior operation has finished.
As Rob said, you can implement Asynchronous Operation class and subclass from it your IoT operation. For me it looks like the most preferred way to implement yr case.
As an alternative, in cases where you need to continue the process only after some asynchronous event in another thread completed, you can use NSCondition. This is a mechanism from obj-c that provide an easy way to wait for a condition to occur.
Here is example:
let cond = NSCondition()
var available = false
var sharedString = ""
class WriterThread: Thread {
override func main() {
for _ in 0..<5 {
cond.lock()
sharedString = "😅"
available = true
cond.signal() // Notify and wake up the waiting thread/s
cond.unlock()
}
}
}
class PrinterThread: Thread {
override func main(){
for _ in 0..<5 { //Just do it 5 times
cond.lock()
while(!available) { //Protect from spurious signals
cond.wait()
}
print(SharedString)
sharedString = ""
available = false
cond.unlock()
}
}
}
let writet = WriterThread()
let printt = PrinterThread()
printt.start()
writet.start()
You could use a DispatchQueue and call .suspend() when you send the operation, and have the code that gets the response call .resume(). Then wherever you want to wait for the response before continuing, just put a dummy queue.sync({ print("done waiting")}) and it will automatically wait until .resume() has been called before printing and continuing.
import Dispatch
var queue = DispatchQueue(label: "queue")
func sendOperationToIoTDevice(){
//send the operation
//...
queue.suspend()
}
...
//whatever code gets the response:
//get response
//...
queue.resume()
...
//main code
sendOperationToIoTDevice()
queue.sync { print("done waiting") } // will hang here until .resume() is called

Synchronization of multiple tasks on single thread

How can I prevent a block of code to be repeatedly accessed from the same thread?
Suppose, I have the next code:
func sendAnalytics() {
// some synchronous work
asyncTask() { _ in
completion()
}
}
I want to prevent any thread from accessing "// some synchronous work", before completion was called.
objc_sync_enter(self)
objc_sync_exit(self)
seem to only prevent accessing this code from multiple threads and don't save me from accessing this code from the single thread. Is there a way to do this correctly, without using custom solutions?
My repeatedly accessing, I mean calling this sendAnalytics from one thread multiple times. Suppose, I have a for, like this:
for i in 0...10 {
sendAnalytics()
}
Every next call won't be waiting for completion inside sendAnalytics get called (obvious). Is there a way to make the next calls wait, before completion fires? Or the whole way of thinking is wrong and I have to solve this problem higher, at the for body?
You can use a DispatchSemaphore to ensure that one call completes before the next can start
let semaphore = DispatchSemaphore(value:1)
func sendAnalytics() {
self.semaphore.wait()
// some synchronous work
asyncTask() { _ in
completion()
self.semaphore.signal()
}
}
The second call to sendAnalytics will block until the first asyncTask is complete. You should be careful not to block the main queue as that will cause your app to become non-responsive. It is probably safer to dispatch the sendAnalytics call onto its own serial dispatch queue to eliminate this risk:
let semaphore = DispatchSemaphore(value:1)
let analyticsQueue = DispatchQueue(label:"analyticsQueue")
func sendAnalytics() {
analyticsQueue.async {
self.semaphore.wait()
// some synchronous work
asyncTask() { _ in
completion()
self.semaphore.signal()
}
}
}

Is it normal that CPU usage exceeds 100% using dispatch async in Xcode 7

I'm a beginner in swift 2, and I'm trying to make my program blocks while showing only a progress spinner until some operation finishes, I made that code snippet in a button with the action "touch up inside", my problem is that while debugging,Xcode 7 CPU usage jumps to 190 % once I tap my button and keeps high until the flag changes its value, Is it normal that CPU usage jumps like that?, also Is it a good practice to use the following snippet or shud i use sleep or some other mechanism inside my infinite loop?
let queue2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
dispatch_async(self.queue2) { () -> Void in
while(flag == true)
{
//wait until flag sets to false from previous func
}
self.dispatch_main({
//continue after the flag became false
})
This is a very economical completion handler
func test(completion:() -> ())
{
// do hard work
completion()
}
let queue2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
dispatch_async(queue2) {
test() {
print("completed")
}
}
or with additional dispatch to the main queue to update the UI
let queue2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
dispatch_async(queue2) {
test() {
print("completed")
dispatch_async(dispatch_get_main_queue()) {
// update UI
}
}
}
This is totally wrong approach as you are using while loop for waiting. You should use Completion Handler to achieve this kind of stuff.
Completion handlers are callbacks that allow a client to perform some action when a framework method or function completes its task. Often the client uses a completion handler to free state or update the user interface. Several framework methods let you implement completion handlers as blocks (instead of, say, delegation methods or notification handlers).
Refer Apple documentation for more details.
I suppose you have a sort of class which manages these "some operation finishes".
When you finish your operations you can comunicate by completion handler or delegation. In the meanwhile you can disable the user interaction of your UI until the end of these operations.
If you provide more informations about your background operations I can add some snippets of code.

Why is popViewControllerAnimated taking so long to run?

I have a secondary viewController that allows me to delete images from the camera roll. The problem is, the completionHandler fires like it's suppose to, but the popViewController doesn't actually seem to run for about 8 seconds. It definitely fires, because I can see the optional output. And I checked just doing the pop, and it runs correctly. I checked the viewWillDisapear event, and it fires late as well, which I expected considering the nav controller hadn't popped the view current viewController yet.
PHPhotoLibrary.sharedPhotoLibrary().performChanges({
PHAssetChangeRequest.deleteAssets(assetsToDelete)
return
}, completionHandler: { success, error in
if success {
println("success")
println(navigationController.popViewControllerAnimated(true))
println("so slow")
}
if let error = error {
println(error)
}
return
})
This is what the documentation says:
Photos executes both the change block and the completion handler block
on an arbitrary serial queue. To update your app’s UI as a result of a
change, dispatch that work to the main queue.
The navigation controller needs to be executed from the main thread, so you need to wrap the call to something like
dispatch_async(dispatch_get_main_queue()) {
navigationController.popViewControllerAnimated(true)
}
For Swift 3
DispatchQueue.main.async() {
self.navigationController?.popViewController(animated: true)
}

My iOS app freezes but no error appears

Does any body know what I need to check if app freezes after some time? I mean, I can see the app in the iPhone screen but no view responds.
I did some google and i found that, i've blocked the main thread somehow.
But my question is how to identify which method causes blocking of main thread? is there any way to identify?
Launch your app and wait for it to freeze. Then press the "pause" button in Xcode. The left pane should show you what method is currently running.
Generally, it is highly recommended to perform on the main thread all animations method and interface manipulation, and to put in background tasks like download data from your server, etc...
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//here everything you want to perform in background
dispatch_async(dispatch_get_main_queue(), ^{
//call back to main queue to update user interface
});
});
Source : http://www.raywenderlich.com/31166/25-ios-app-performance-tips-tricks
Set a break point from where the freeze occurs and find which line cause that.
Chances may be,Loading of large data,disable the controls,overload in main thread,Just find out where that occurs using breakpoints and rectify based on that.
I believe it should be possible to periodically check to see if the main thread is blocked or frozen. You could create an object to do this like so:
final class FreezeObserver {
private let frequencySeconds: Double = 10
private let acceptableFreezeLength: Double = 0.5
func start() {
DispatchQueue.global(qos: .background).async {
let timer = Timer(timeInterval: self.frequencySeconds, repeats: true) { _ in
var isFrozen = true
DispatchQueue.main.async {
isFrozen = false
}
DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + self.acceptableFreezeLength) {
guard isFrozen else { return }
print("your app is frozen, so crash or whatever")
}
}
let runLoop = RunLoop.current
runLoop.add(timer, forMode: .default)
runLoop.run()
}
}
}
Update October 2021:
Sentry now offers freeze observation, if you don't wanna roll this yourself.
I reached an error similar to this, but it was for different reasons. I had a button that performed a segue to another ViewController that contained a TableView, but it looked like the application froze whenever the segue was performed.
My issue was that I was infinitely calling reloadData() due to a couple of didSet observers in one of my variables. Once I relocated this call elsewhere, the issue was fixed.
Most Of the Time this happened to me when a design change is being called for INFINITE time. Which function can do that? well it is this one:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
}
Solution is to add condition where the function inside of viewDidLayoutSubviews get calls only 1 time.
It could be that another view is not properly dismissed and it's blocking user interaction! Check the UI Debugger, and look at the top layer, to see if there is any strange thing there.

Resources