In my iPad App, am connecting to a web service. Whilst connecting to it, am displaying the progress activity indicator and a corresponding message to the user in a label (the label is in a subview and am adding the subview to the current view).
After this line of code (which calls a method to add the subview to the view), am invoking the method to call the web service. However, the web service call is getting executed first, and then only the user-information subview is displayed.
Is there any way to say that I want to 'continue displaying' the alert view even while the execution continues to the next line of code?
// Calling method to add info/alert subview to current view [self displayUserMessage];
// Connect to Web Service [self connectToWebService];
I'm not sure if I totally understand your question. Also it's far more easy to understand if you provide some code after your explanation... Anyway what I understand is that you are connecting to a web service and showing some info while the connection is on going?
Remember that if you don't want to hang your user interface you need to send the webService Connection in another thread, so you can keep the main thread free. You can do so using GCD.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
[self connectWithWebService];
});
Then depending on the architecture of the web service, you can use a delegate o maybe a completion block to show some messages (info/alert) to the user. In that case remember that anything related to UI should run on the main thread. So as I said before depending on your architecture you should do something like this
dispatch_async(dispatch_get_main_queue(), ^{
// Show UI Changes
});
The UI should update properly while the webService method is running on background.
If you want asynchronous connections its easier to go with NSURLConnection's sendAsynchronousRequest:queue:completionHandler:..
you can display your alert before calling it and dismiss it in the completion handler.
Related
I have some long-running startup tasks (like loading Parse objects from local data storage) in my app. Those tasks should be finished before the interface starts to appear. The app was originally created using storyboards, so the interface starts to appear automatically after application:didFinishLaunchesWithOptions: method finishes. I can't block main thread because Parse SDK fires all it's callbacks on main thread (so blocking results in deadlock). I also need to delay return from application:didFinishLaunchesWithOptions: to finish setup. So what I did is:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Dispatch long-running tasks
dispatch_group_t startup_group = dispatch_group_create();
dispatch_group_async(startup_group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Perform some long-running setup here
});
// Run main run loop until startup tasks finished (is it OK to do so?)
while (dispatch_group_wait(startup_group, DISPATCH_TIME_NOW))
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
return YES;
}
Is it the proper usage of NSRunLoop? Are there any potential caveats? Can you propose more elegant (preferably GCD) solution?
UPDATE:
Loading Parse object from local data storage (i.e. loading tiny file from SSD) is not so long operation as loading something from the web. The delay is barely noticeable, but long enough to trigger warnBlockingOperationOnMainThread, so UX is not an issue.
The real issue is (infrequent) crashes caused by spinning another main runloop above regular main runloop, which sometimes leads to reentering UIApplication delegate methods, which apparently is not thread-safe.
Of cause introduction of splash screen is an obvious solution, but I was looking for a something more simple and elegant. I have a feeling it exist.
While this technically works depending on what your setup is doing (for example web service calls) the application may never launch or launch with unexpected results.
The more user friendly method would be to add a "loading" view controller to your storyboard which would be your landing view. Perform your long running setup here while providing the user with information/status/whatever is appropriate, then push your original view controller.
Imho I think it's better to do your stuff in your main View Controller and show something to user like a custom spinner: in this way you can download your data and the user know what happens.
It's never a good idea take so long time to launch an app with a blank screen or just a launch screen with no info for the user.
This is creative but a really bad idea. You should make your UI load in a failsafe state - in other words, assume the network is not available/slow, and show a view that won't explode if it doesn't get data before being rendered. Then you can safely load data on a background queue and either update the view or transition to the data-centric view once it is done loading.
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
//Load your data here
//Dispatch back to the main queue once your data is loaded.
dispatch_async(dispatch_get_main_queue(), ^{
//Update your UI here, or transition to the data centric view controller. The data loaded above is available at the time this block runs
});
});
Lagging the return of didFinishLaunchingWithOptions: is really a bad idea.This will give the bad impression of your app and its a bad design.
Instead you could land in the view controller and show a overlay view that shows the progress or activity you are doing when completed you could dismiss it.This is the right approach.
Here is a link to Third party library that eases showing/hiding the overlay view and setting some text on the view.
You can find so many libraries like this just google iOS HUD.HUD means Heads Up Display or you can design the custom thing yourself using UIAnimation easily.
Generally speaking,
I have to appreciate that code you have written in the sense you have stopped the current flow or run loop using While and NSRunloop.
May be you can alter your code little bit and use in some other place of your program to stop the current flow or runloop and wait for something.
NSDate *runloopUntill = [NSDate dateWithTimeIntervalSinceNow:0.1];
while (YourBoolFlag && [[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode runloopUntill])
The following apple documentation will give you deep insights in NSRunloop 1 2
I would to do a remote call before the app is started (in AppDelegate, when the splash screen is showed). Then i would to choose which view controller to load based on url response.
Is right to do this on AppDelegate? Or I need a different approach?
I think the best approach is creating a ViewController where you make this choice. Once this VC is loaded you make your remote call while showing in the UI that your app is actually working and waiting for a network response - the best approach is probably showing a message with a UIActivityIndicatorView spinning.
Once you get the response you load the VC that you need. You should also handle errors - what are you going to show if the network request fails? Are you showing an error message?
You should not do any synchronous network calls from willFinishLaunchingWithOptions. If you take more than a few seconds to return that method or (didFinishLaunchingWithOptions, or the other app delegate methods that the system calls in the process of launching your app) then the springboard will terminate your app as unresponsive.
#Tanzolone has the right idea. Have your app display a view controller that shows your app's UI, THEN invoke the network request and decide what second screen to switch to based on the response.
I am trying to display a new view in IOS after login in my application. I also want to display this view when I start the application if the user is already logged in this app.
Now, if I start the application the first time I have the login view and I can write my user name and my password. when I press send button(login) the next controller starts. I would like to set for 2 or 3 seconds one view while I am downloading the information from the web service. I don't care if the information comes faster than the two seconds because I want to wait this time. I save the user information in NSUserdefaults.
Next time I start the app I already have the information and I go directly to the next viewcontroller without see any login view. So I want to watch the loading view.
I use AFNetworking to consume my web services.
You have two options, it seems a little arbitrary to show it for 2 seconds, but you can present the view and then:
[myView performSelector:#selector(setHidden:) withObject:[NSNumber numberWithBool:YES] afterDelay:3.0];
Or what you probably should do is hide the view or set it to nil within the completion block of your AFNetworking Call, that way if the call takes longer than 2-3 seconds, the view won't disappear
You can call the method for hiding view after delay using this:
[self performSelector:#selector(hideView) withObject:nil afterDelay:3.0];
Here is an idea, using GCD:
Store the login information into keychain (NSUserDefaults are just plain unsafe for passwords!) and dismiss the login window immediately after it is saved.
When the loding view is shown, create a dispatch group and submit your login and download logic there. (com.example.login thread)
Use NSTimer (or any appropriate logic) to wait for 3 seconds, then spawn another thread in GCD (com.example.login-wait thread), call dispatch_group_wait to block that thread until the com.example.login thread is done, and submit a block back to the main queue to switch views.
I used this login in multiple applications and it looked fantastic.
So I am trying to display an animated loading view (custom built) that will continue to animate while making a synchronous request to a web service.
I am familiar with synchronous and asynchrous requests using NSURLConnections and delegates, but my problem is that I want to ensure that my thread WAITS for the request to finish BUT still animates my loading indicator.
Are there any suggestions as to what the best way to go about this is?
Doing this with a synchronous request is not the way to go -- if you want your app to "wait" for the request to finish, you just put whatever code you want to resume after the request finishes in the connectionDidFinishLoading: method. That way, your UI will not be stalled and you can animate your loading indicator.
I'm making an app that loads data asynchronously for a data picker, then reloads the data picker, and everything's fine.
The problem is that if the user taps on the text field, the app makes the network request for the info, but if it hasn't loaded and then the user taps the back button, then the view disappears, but the data is still loading in the background.
I've tried running this loading method on a custom thread and then calling [myThread cancel]; on viewWillDisappear, but the data still loads. I want to avoid the user using data when he cleary doesn't want to load it anymore. Any help?
I was thinking of killing com.apple.nsurlconnectionloader but I have no idea how to do that.
Few things to note here, canceling an NSThread will not terminate it immediately, it will only mark it for termination.
What I would do instead is implement your NSURLConnection delegates within an NSOperation, and when you want to cancel your download you can call cancel on the operation, which will set its isCanceled property to YES. You can then use this as a flag to terminate the operation's runloop, and the download will not continue.
Apple has an excellent technote on using NSOperation here https://developer.apple.com/library/ios/#technotes/tn2109/_index.html