I'm nervous that you accepted the answer regarding sendSynchronousRequest
from the background queue because, from a practical perspective, this is no different than your didReceiveData
-based implementation. Specifically, if you do perform synchronous request from a background queue, it will not make "everything else wait".
But if you neglect to do this synchronous request from the background queue (i.e. if you do it from the main thread), you end up with a horrible UX (the app is frozen and the user is wondering whether the app has crashed), and worse, your app could be killed by the iOS "watchdog process" if it takes too long.
With all deference to the various answers, sending a synchronous request on a background queue is indistinguishable from the existing NSURLConnectionDataDelegate
-based approach. What you really need to do is accept the fact that the rest of the app will not freeze, and therefore simply update the UI to let the user know what's happening, namely that (a) provide some visual cue that the app is not dead; and (b) prevent the user from interacting with your existing UI.
For example, before issuing your network request, add a view that will cover/dim the rest of your UI, prevent user interaction with the existing UI, and add a spinner to let the user know that the app is busy doing something. So, define a class property for a UIView
that will dim the rest of the UI:
@property (nonatomic, strong) UIView *dimView;
And then, before the network request, update the UI:
// add a view that dims the rest of the UI, so the user has some visual cue that they can't use the UI yet
// by covering the whole UI, you're effectively locking the user out of using the app
// until the network request is done
self.dimView = [[UIView alloc] initWithFrame:self.view.bounds];
self.dimView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.5];
self.dimView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.view addSubview:self.dimView];
// add a spinner that shows the user that something is really happening
UIActivityIndicatorView *indicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
indicatorView.center = self.dimView.center;
indicatorView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;
[indicatorView startAnimating];
[self.dimView addSubview:indicatorView];
[self callWebService:URL withSOAP:SOAP];
And then, in the connectionDidFinishLoading
method (or if you used sendSynchronousRequest
from a background queue, in the latter portion of code to dispatched to that background queue), after you finish parsing your data, you want to:
[self.dimView removeFromSuperview];
self.dimView = nil;
// now do what ever to need do to update your UI, e.g.:
//
// [self.tableView reloadData]
But the key is that you absolutely do not want to issue a synchronous network request from the main queue. You should (a) do your network request asynchronously (either synchronously on a background queue, or as you originally implemented) so that the iOS watchdog process doesn't kill your app; (b) let the user know the app is making some network request and hasn't frozen on them (e.g. a UIActivityIndicatorView
; and (c) when the request is done, remove these "the app is doing network request" UI elements and refresh the rest of the UI now that your network request is done.
Finally, when testing your app, make sure you test it in real-world networking situations. I'd suggest you install the Hardware IO tools (available in Xcode menu, under "Open Developer Tools" - "More Developer Tools") and check out the Network Link Conditioner. This lets you simulate real-world network situations (e.g. a bad 3G or Edge network condition) on the iOS simulator. We get lulled into a false sense of performance when we test our apps in typical development environments with ideal network connectivity. Devices "in the wild" suffer a wide range of degraded network situations, and it's good to test your app in a similar, suboptimal network situation.