My iOS app makes a request to a server with php scripts hitting a database, and sending back some data. The db hits are pretty straight forward, just fetching some data based on a unique key.
It works fine on wifi (less than a second delay), but is slow to the point of being unuseable on 3g. Can anyone advise what may be some causes, or where I can explore more? I would imagine apps like facebook or googlemaps are far more data intensive than what I'm doing, yet they seem to respond much faster. I'm probably sending no more than 20-30 characters to the server (plus whatever overhead JSON adds), and getting back ~500-1000 characters (plus whatever overhead JSON adds).
I'm sending things synchronously, but I don't think async won't help me...the data won't appear until i get the message back anyway.
My code for sending and recieving is:
+ (id)queryDBJSON:(NSString*)script inputs:(id)post
{
NSError *error;
NSData *jsonPayload = [NSJSONSerialization dataWithJSONObject:post options:NSJSONWritingPrettyPrinted error:&error];
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@", script]];
NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:url];
[theRequest setHTTPMethod:@"POST"];
[theRequest setHTTPBody:jsonPayload];
NSHTTPURLResponse* urlResponse = nil;
NSData *responseData = [NSURLConnection sendSynchronousRequest:theRequest returningResponse:&urlResponse error:&error];
if ([urlResponse statusCode] != 200)
{
return nil;
}
NSString *strResult = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
NSData *jsonData = [strResult dataUsingEncoding:NSUTF8StringEncoding];
id aa = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&error];
return aa;
}
Thanks for any inputs!
The problem in these cases isn't the bandwidth of the network, but rather it's the network latency (a small request still takes a long time). And the unfortunately reality is that you can't get around network latency issues. So, a couple of observations:
As rmaddy says, you simply must do your network operations asynchronously (either with synchronous calls in a background queue or using asynchronous methods). If you don't and your app fails to respond in a timely manner, not only will you suffer a UX with unacceptable delays, but if it takes long enough, iOS may even kill your app. If you don't want iOS killing your app when there is a high network latency, you must use asynchronous network operations.
Beyond the obvious (and necessary) solution of doing asynchronous network operations, you generally cache previous result sets (so rather than just showing the user a UIProgressView
and stopping all interaction with your app until the asynchronous calls are done, you can show the user the last good results). If you look at apps like Facebook (or, really, any well executed app that interacts with the network), you'll see that they show you what you saw when you last visited that page, they give you some visual indication that further network operations are in progress (e.g. at the very least, the spinning network activity indicator in the status bar), and as new results come in over the network, they then update the view accordingly.
A more sophisticated approach is to initiate the asynchronous network requests that the user will need for subsequent operations as soon as the app starts (rather than waiting until the user navigates to the screen for which that network data is needed). This only works in those cases where the user is navigating to different parts of your app that need to make different network requests, but it's a wonderful way to isolate the user from the network latency in those cases.
If you absolutely can't cache previous result sets for a Facebook-like UX, then still do your network operations asynchronously, but present the user with UI that makes it clear that network operations are in progress (e.g. a UIProgressView
with perhaps on a black UIView
with a 50% alpha that covers the rest of the screen). That gives the user a visual indication that your app can't present results until the network operation is done. And because you're doing the operations asynchronously, iOS won't kill your app.