Network calls take more time on iOS than on Android - ios

I have a very strange problem, in Android, the API calls take 300-500 ms while in iOS it takes 1.5-2.5 sec. I have removed dependencies like my server, device specific issue, internet connectivity etc. I have a very simple sample code hitting a sample URL and for me, it takes about 2 sec, even on the simulator.
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(#"start");
// 1
NSURL *url = [NSURL URLWithString:#"https://httpbin.org/get"];
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
// 2
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
[request setValue:#"" forHTTPHeaderField:#"Accept-Encoding"];
request.HTTPMethod = #"GET";
[[session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
// Handle response here
NSLog(#"end - %ld", (long)[(NSHTTPURLResponse*)response statusCode]);
}] resume];
});
I also have tried using AFNetworking and ASSIHTTP libraries, but there is no difference. I also have checked the headers and they are the same in both Android and iOS. What am I doing something wrong here?

I think your problem is not in the network but in this line:
dispatch_async(dispatch_get_global_queue(0, 0), ^{
Can you remove it and check again?
Data task is async so you don't need to wrap it in another async block.
Also you don't need to create instance of NSURLSession for every request

You can log out the timestamp (NSLog already did) when the request generate, send, callback to analyse it.

Does the server side code do any processing based on 'user-agent'?
Is there a time difference if you open the url in iOS safari and within the app ?
You can try calling the api from postman (or another REST API test tool like firefox RESTClient) and override the user-agent to use iOS values (http://www.enterpriseios.com/wiki/UserAgent). If the time difference is still the same, theres nothing you can do in your mobile code to fix this lag.
P.S. :
1. Overriding user-agent in postman needs some tweaking : https://github.com/postmanlabs/postman-app-support/wiki/Postman-Proxy

Related

Make http DELETE request with NSURLSession

I'm trying to make an http DELETE request using NSURLSession, but it's not completely working. The server deletes the resource, but the NSURLSession method dataTaskWithRequest: completionHandler: returns a time out error after waiting for the specified timeout.
I am not using NSURLConnection because it is deprecated.
Of the NSURLSession methods to use, I chose dataTaskWithRequest because it is most similar to the method I use for http GET: dataTaskWithUrl: completionHandler. The methods beginning with "uploadTask" and "downloadTask" don't seem appropriate for a DELETE, but downloadTaskWithRequest: completionHandler: 'worked' in the same way as the dataTask method above. The server deleted the resource, but the method returned a time out error.
Here is the code:
+(void)httpDelete: (NSString*)url completionHandler: (void(^)(id, NSError*))complete
{
NSURLSessionConfiguration *urlSessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSMutableDictionary* dictionaryAdditionalHeaders = [[NSMutableDictionary alloc] init];
NSString* stringBearerToken = #"...";
NSString* stringApiKey = #"...";
[dictionaryAdditionalHeaders setObject:stringBearerToken forKey:#"Authorization"];
[dictionaryAdditionalHeaders setObject:stringApiKey forKey:#"x-api-key"];
[dictionaryAdditionalHeaders setObject:#"application/json" forKey:#"Content-Type"];
[dictionaryAdditionalHeaders setObject:#0 forKey:#"Content-Length"];
[urlSessionConfiguration setHTTPAdditionalHeaders: dictionaryAdditionalHeaders];
NSURLSession *urlSession = [NSURLSession sessionWithConfiguration: urlSessionConfiguration delegate:nil delegateQueue:[NSOperationQueue mainQueue]];
NSMutableURLRequest* mutableUrlRequest = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:url] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:5];
[mutableUrlRequest setHTTPMethod: #"DELETE"];
[[urlSession dataTaskWithRequest:mutableUrlRequest completionHandler: ^(NSData *data, NSURLResponse* response, NSError* error)
{
if(error != nil)
{
complete(response, error);
}
else
{
complete(response, nil);
}
}] resume];
}
Using Postman, the DELETE call returns with a 204 immediately.
Am I using NSURLSession correctly for a delete request?
It turns out the Amazon API Gateway incorrectly sends a Content-Length header with a 204 response. They added the issue to their backlog March 21, 2016 according to this AWS forum. When I increased the timeout interval of the NSMutableURLRequest to a ridiculous 300 seconds, the dataTaskWithRequest method returns with a real response instead of timing out.
This isn't an error with NSURLSession - it means that your request is actually timing out. That means that there's an error on the back-end (maybe it's not reaching your server at all?)
Also, I've found these issues much easier to debug using a third-party framework to send my HTTP requests. AFNetworking is a really good one.

Http/2 server_push with NSURLSession

I have implemented a program to communicate http2 using NSURLSession of iOS9, And it can communicate with my server in http2.
However, I'm having a problem with receive server_push.
I found ENABLE_PUSH value is 0 in their settings and there's no delegate in receive server push in NSURLSession...
・I think NSURLSession doesn't support server_push. Is this right?
・If it support server_push,how to use?
/**
It WORKS for post data and get response.
I don't know the code should be added here
in order to get the server_push.
I suspect that NSURLSession itself cannot receive the server_push(;_;)
**/
- (void) postData
{
NSString *urlstr = self.urlArea.text;
NSURL * url = [NSURL URLWithString:urlstr];
NSDictionary *params = #{#"data":#""};
//json to query
NSData *query = [self buildQueryWithDictionary: params];
//make request
NSMutableURLRequest *request = [NSMutableURLRequest
requestWithURL:url
cachePolicy: NSURLRequestReloadIgnoringCacheData
timeoutInterval: 10.0];
[request setHTTPMethod: #"POST"];
[request setHTTPBody: query];
//prepare session
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
//resume
[[session dataTaskWithRequest: request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
if (response && ! error) {
NSLog(#"Data: %#", [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding]);
}else {
NSLog(#"ERR: %#", error);
}
}] resume];
}
I read this here:
The HTTP2 push mechanism is not a generic server push mechanism like
websocket or server sent events.
It is designed for a specific optimisation of HTTP conversations.
Specifically when a client asks for a resource (eg index.html) the
server can guess that it is going to next ask for a bunch of
associated resources (eg theme.css, jquery.js, logo.png, etc. etc.)
Typically a webpage can have 10s of such associated requests.
With HTTP/1.1, the server had to wait until the client actually sends
request for these associated resources, and then the client is limited
by connections to only ask for approx 6 at a time. Thus it can take
many round trips before all the associated resources that are needed
by a webpage are actually sent.
With HTTP/2, the server can send in the response to the index.html GET
push promises to tell the client that it is going to also send
theme.css, jquery.js, logo.png, etc. as if the client had requested
them. The client can then cancel those pushes or just wait for them to
be sent without incurring the extra latency of multiple round trips.
ere is a blog about the push API for HTTP2 and SPDY in jetty:
https://webtide.com/http2-push-with-experimental-servlet-api/
Solved
I received following reply from support.
iOS does not currently support this.
update
(#vin25 comment)
ios10 supports it.

Issue with NSURLSession and Content-Type

I am trying to upload a photo to a webservice, and I am using the following code:
NSData *imageData = UIImageJPEGRepresentation([UIImage imageNamed:imageToUpload], 0.5);
// 1
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
config.HTTPMaximumConnectionsPerHost = 1;
[config setHTTPAdditionalHeaders:#{#"Content-Type":#"multipart/form-data"}];
// 2
NSURLSession *upLoadSession = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
// for now just create a random file name, dropbox will handle it if we overwrite a file and create a new name..
NSString *urlString =[NSString stringWithFormat:#"https://ABC.co.uk/photos?api_key=MYAPIKEY" ];
NSURL *webUrl = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:webUrl];
[request setHTTPMethod:#"POST"];
// 3
NSURLSessionUploadTask *uploadTask = [upLoadSession uploadTaskWithRequest:request fromData:imageData];
// 4
// uploadView.hidden = NO;
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
// 5
[uploadTask resume];
I receive an error 500, and I'm not sure what to do.
I have tried implementing AFNetworking, body data appending like This, This, and This. Yet, no luck.
The code above is from Ray Wenderlich's website: Here.
All I need to provide the server is this:
file (the image)
API Key (I tried using the NSMutableURLRequest's addValue:forHTTPHeaderField: but it didn't work, so I'm using it in the URL. I know it's not the best practice in terms of safety, but I'm getting so desperate as I've been working on this for over 10 hours! One step at the time!)
The Content-Type to be multipart/form-data
That's all I need to provide, and yet I'm not getting anywhere!
Can anyone please help?
Thank you in advance
UPDATE 1: So I made some alterations to the URL structure, and added the API key as a dictionary in HTTPBody, and now I am getting error 403.
It seems really weird that apple has not made any effort to serialise this part of connection. Anyway. I really appreciate anyone helping out!
Try setting the Content-Type in the request as well:
[request addValue:#"multipart/form-data" forHTTPHeaderField:#"Content-Type"];

Cookies handling with JSONModel/JSONHTTPClient in iOS

In my current project I am using, +(void)postJSONFromURLWithString:(NSString*)urlString params:(NSDictionary*)params completion:(JSONObjectBlock)completeBlock; method to create account and log in my application for the very first time . For second time and onwards, log in call is not there, application directly opens the user's profile screen. But when I am updating user profile (name, contact number etc.), I am getting response status code 403 from by the statement
NSLog(#"Response status code = %i", (int)response.statusCode);
added in implementation of method
+(NSData*)syncRequestDataFromURL:(NSURL*)url method:(NSString*)method requestBody:(NSData*)bodyData headers:(NSDictionary*)headers etag:(NSString**)etag error:(JSONModelError**)err
403 is generally invoked due to authorization failure in server side.
Is there any way to see what are the cookies are going to server side while I am making an API call with
+(void)postJSONFromURLWithString:(NSString*)urlString params:(NSDictionary*)params completion:(JSONObjectBlock)completeBlock;?
JSONModel's built-in networking support is intentionally very basic/primitive/limited. It does not support custom authentication, cookies, etc.
You'd be best making a request with NSURLSession to get the data, then use JSONModel just for the deserialization - rather than for the whole request.
e.g.:
NSURLSession *session = [NSURLSession sharedSession];
NSURL *url = [NSURL URLWithString:#"https://example.com/mydata.json"];
NSURLSessionDataTask *task = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
MyModel *obj = [[MyModel alloc] initWithData:data error:nil];
}];
[task resume];

Persistance of data in NSURLSession

I have a bunch of nested NSURLSessionDataTasks and the data downloaded persists on calls of the method. I think it might have something to do with how NSURLSession handles cache??
Is there some way I can flush the cache so I get the most recent data when I call the method (thats supposed to refresh the data)
Here's the code I'm working with if it helps at all...
--EDIT--
And a more readable excerpt of just one of the requests that I want to flush the cache from after use:
NSMutableURLRequest *homeRequest = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:[NSString stringWithFormat:#"https://mistar.oakland.k12.mi.us/novi/StudentPortal/Home/Login"]]];
[homeRequest setCachePolicy:NSURLRequestReloadIgnoringCacheData];
[homeRequest setHTTPMethod:#"POST"];
NSString *postString = [NSString stringWithFormat:#"Pin=%#&Password=%#",
[self percentEscapeString:pin],
[self percentEscapeString:password]];
NSData * postBody = [postString dataUsingEncoding:NSUTF8StringEncoding];
[homeRequest setHTTPBody:postBody];
NSURLSessionDataTask *homeTask = [defaultSession dataTaskWithRequest:homeRequest
homeTask is the NSURLDataTask that I add to the defaultSession. I want to remove the cache it saves so that when I call the method that contains these lines again, it fetches from the server instead of the local cache.
Is defautSession a local variable pointing to [NSURLSession sharedSession]? I would try using an ephemeral session instead. It is almost the same except that it doesn't cache data to disk.
NSURLSessionConfiguration *config =
[NSURLSessionConfiguration ephemeralSessionConfiguration];
NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration:config];
If that still doesn't fix the problem, then invalidate it before posting again. Use
[defaultSession invalidateAndCancel]
after your task has completed and then create a new session before the next time you need to post.

Resources