How to execute statements in order in objective c - ios

When i tap on my button, my function was called
[myBtn addTarget:self action:#selector(myFunction) forControlEvents:UIControlEventTouchUpInside];
In my function, a collection of complex statement will be executed and take a litte bit time to run, so i want to show Loading (UIActivityIndicatorView) as the following:
-(void) addTradeAction {
//Show Loading
[SharedAppDelegate showLoading];
//disable user interaction
self.view.userInteractionEnabled = NO;
//execute call webservice in here - may be take 10s
//Hide Loading
[ShareAppDelegate hideLoading];
}
When tap on myBtn (my Button) -> after 3s or 4s, [ShareAppDelegate showLoading] was called.
It is unusual when i use [ShareAppDelegate showLoading] on other Function, -> it work very nice, i mean all the statement be executed in order.
All i want, when i tap on My Button, Loading will be called immediatelly.
Tks in advance

A correct way to perform a tasks in background, and in your case showing an activity indicator, is :
-(void)myBackGroundTask
{
//here showing the 'loading' and blocking interaction if you want so
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
});
});
}
With this kind of block, you are sure that your interface do not freeze, and keep a smooth animation.

If your complex statements do not any UI animations or UI related code, then you can execute that part in a different thread(other than the mainThread). Once the statements are done(or in completion block), you can remove the loadingOverlay there.

Put myFunction to run on a background queue as it probably makes the system hang:
- (void)myFunction {
dispatch_queue_t myQueue = dispatch_queue_create("myQueue", NULL);
// execute a task on that queue asynchronously
dispatch_async(myQueue, ^{
// Put the current myFunction code here.
});
}

Related

dispatch_async handle event

I know dispatch_async can handle thread.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// handle things that takes a long time
dispatch_async(dispatch_get_main_queue(), ^{
// update UI
});
});
But How can I cancel it the thread?
For example:
I have two viewcontrollers- AViewController and BViewController, A->present B, and a button to dismiss B.
I'll do myMethod in BViewController, the things is I want to dismiss B when exec myMethod, so I use dispatch_async, it did dismiss from B to A. but after going back to AViewController, here comes the audio.
How can I stop the myMethod after dismiss method exec immediately?
- (void)myMethod {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// handles thing A
// play a audio
dispatch_async(dispatch_get_main_queue(), ^{
// update UI
});
});
}
What you need for this is not GCD and dispatch_async, but an NSOperation. It is a high-level wrapper around GCD that allows you to have references to your async operations, check their status, cancel them, etc.
I recommend using a very handy subclass named NSBlockOperation. You should keep a reference to your operation in your class, and if you need to cancel it, just call [self.myOperation cancel].

Loading of a View with an UIActivityIndicator

I have an UITableView with some UITableViewCells and when I click on a specific cell the application will download some information from my server. The question is: "How can I show IMMEDIATELY* a view with only an UIActivityIndicator which is animated during all the time of the download and stops its animation when the download is complete?"
*Note: It should not be after other strange operations from the app, it must be the first thing after the click on the cell.
You can use the below method which starts and stops the activity indicator on main thread in a single method
- (void)showIndicatorAndStartWork
{
// start the activity indicator (you are now on the main queue)
[activityIndicator startAnimating];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// do your background code here
dispatch_sync(dispatch_get_main_queue(), ^{
// stop the activity indicator (you are now on the main queue again)
[activityIndicator stopAnimating];
});
});
}
Note:
I am doing some background stuff so I have used dispatch_async, if you also want to download something in background you can also use dispatch_async else you can also download the stuff on main thread.

Objective-C - Wait for call on another thread to return before continuing

In my iOS application, I have a database call that takes some time to complete. I have a spinner visible on the screen while this operation is taking place. I am hitting an error with the app crashing with "com.myapp failed to resume in time" so it seems like it is running the database call on the main thread, causing issues.
Current Code
-(void)timeToDoWork
{
...
[CATransaction flush];
[[DatabaseWorker staticInstance] doWork];
//Additional UI stuff here
...
if([self->myReceiver respondsToSelector:self->myMessage])
{
[self->myReceiver performSelector:self->myMessage];
}
}
To get the doWork function to take place on a background thread, it looks like I can use Grand Central Dispatch:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
[[DatabaseWorker staticInstance] doWork];
});
However, how do I prevent the execution from continuing until it is complete? Should I end the method after the doWork call, and move everything below it to a new function?
Sample
-(void)timeToDoWork
{
...
[CATransaction flush];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
[[DatabaseWorker staticInstance] doWork];
dispatch_async(dispatch_get_main_queue(), ^{
[self doneDoingWork];
});
});
}
-(void)doneDoingWork
{
//Additional UI stuff here
...
if([self->myReceiver respondsToSelector:self->myMessage])
{
[self->myReceiver performSelector:self->myMessage];
}
}
Is there a better way to do this?
Prevent execution in main thread from continuing is really bad idea. iOS will terminate your application since main thread should always work with run loop.
I suggest you following way to handle your problem:
Write a "Locker". Let it show some view with animated spinner and no buttons at all.
When you start dispatch async operation just bring it to the front and let it work with run loop.
When your async operation completes close the locker.
You can also use blocks.
e.g..
- (void)doWorkWithCompletionHandler:(void(^)())handler {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
// do your db stuff here...
dispatch_async(dispatch_get_main_queue(), ^{
handler();
});
});
}
And then use it like that:
[[DatabaseWorker staticInstance] doWorkWithCompletionHandler:^{
// update your UI here, after the db operation is completed.
}];
P.S.
It might be a good idea to copy the handler block.
The error you are receiving suggests that you are doing something in application:didFinishLaunchingWithOptions: or applicationDidBecomeAction: or somewhere else in the launch cycle that is taking too long and the app is getting terminated by the launch watchdog timer. Above all, it is vital that you return as quickly as possible from these methods. I'm not sure where your code fits into the launch cycle; but this explanation seems plausible.
There are all sorts of ways to address this; but taking the lengthy process off the main queue is the first step as you noted. Without knowing more about what main queue objects (e.g. UI) depend on this database transaction, I'd say that your suggested solution is perfectly fine. That is, dispatch the work to a background queue; and on completion dispatch the remaining UI work to the main queue.
Delegates were suggested elsewhere as a solution. That's also workable although you still have to concern yourself with which queue the delegate methods get called on.
I think that you should use a delegate in your DatabaseWorker and the method doWork always run in background, so when the worker finish the work it tell to its delegate that the work is finished. The delegate method must be called in the main thread.
In the case that you have many objects that need to know when the DatabaseWorker finish instead to use a delegate I would use notifications.
EDIT:
In the DatabaseWorker class you need to implement the method doWork like this:
- (void) doWork{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
//Do the work.
dispatch_async(dispatch_get_main_queue(), ^{
[self.delegate finishWork];
});
});
}
And in the class that implement timeTodoWork:
-(void)timeToDoWork
{
...
[CATransaction flush];
[[DatabaseWorker staticInstance] setDelegate:self];
[[DatabaseWorker staticInstance] doWork];
}
#pragma mark DatabaseWorkerDelegate
- (void) finishWork{
//Additional UI stuff here
...
if([self->myReceiver respondsToSelector:self->myMessage])
{
[self->myReceiver performSelector:self->myMessage];
}
}
Also you can use:
[self performSelectorInBackground:#selector(doWorkInBackground) withObject:nil];
instead of:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
//Do the work.
});
And add a method:
- (void) doWorkInBackground{
//Do the work
[self.delegate performSelectorOnMainThread:#selector(finishWork) withObject:nil waitUntilDone:NO];
}

How to update UIView before next method is called in Objective-C

I'm trying to update my textView on screen before it starts downloading data. Right now, it only updates the view after all of the downloads are complete. How can I do it before or in between the downloads?
Edit: I want the self.textView.text = #"Connection is good, start syncing..."; to update the UI before the downloading starts. But right now, it only updates after the download finishes.
Here is what the code looks like.
if ([self.webApp oAuthTokenIsValid:&error responseError:&responseError]) {
self.textView.text = #"Connection is good, start syncing...";
[self.textView setNeedsDisplay];
[self performSelectorInBackground:#selector(downloadCustomers:) withObject:error];
}
I'm new to this and have yet to learn how threads work, but from what I read, the downloadCustomers function should be using a background thread leaving the main thread to update the UI.
if ([self.webApp oAuthTokenIsValid:&error responseError:&responseError]) {
self.textView.text = #"Connection is good, start syncing...";
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self downloadCustomers];
dispatch_async(dispatch_get_main_queue(), ^{
//Do whatever you want when your download is finished, maybe self.textView.text = #"syncing finished"
});
});
}
The pattern here is to initialize your download on background thread and then call back to main thread for UI update.
Below is an example using GCD. The advantage of GCD version is that you can consider using whatever you do in -downloadCustomers, to insert in-line where you call it.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self downloadCustomers];
dispatch_async(dispatch_get_main_queue(), ^{
[self.textView setNeedsDisplay];
});
});

How to hide a UIPopoverController while a lengthy operation is to be performed?

After selecting an option from a popover controller, the delegate is informed that a selection has been made.
I want to dismiss the popover, have it removed from the screen, and display an activity indicator to the user.
Unfortunately, the code below the dismissPopover runs before the popover actually disappears, resulting in a long wait without anything appearing to be happening.
- (void)itemSelected:(int)option {
[popController dismissPopoverAnimated:YES];
activityIndicator.hidden = NO;
[activityIndicator startAnimating];
switch (option) {
case 0:
// Do something that takes some time
break;
case 1:
// Do something that takes even longer
break;
}
}
What's the best way to return control back to the calling ViewController after dismissing the popover?
The problem is that when you change the UI, it doesn't happen instantly. The changes are actually queued up to occur next time the main event loop finishes. Since that usually happens right away, we usually don't have to worry about the difference. All UI updates happen on the main thread, and since your long operations are also on the main thread, the app never gets around to updating the UI until the long operations are done.
One solution would be to use Grand Central Dispatch to offload those operations to another thread, which will allow the main thread to continue executing (and the UI to continue updating) until the operation is done.
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
[self performReallyLongOperation];
});
dispatch_release(queue);
You can use UIPopOverController's delegate method popoverControllerDidDismissPopover to execute your code after the popover is done dismissing:
Header
<UIPopoverControllerDelegate>
Implementation
- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController
{
activityIndicator.hidden = NO;
[activityIndicator startAnimating];
switch (option) {
case 0:
// Do something that takes some time
break;
case 1:
// Do something that takes even longer
break;
}
}

Resources