I noticed that my application is connecting to remote resources way slower than web browsers, or cURL and decided to do some digging.
I've been using NSURLSession sessionWithConfiguration:delegate: to handle server responses, and decided to try instead using NSURLSession dataTaskWithRequest:completionHandler:. The test methods are below:
-(void)connectWithDelegate:(NSString *)url
{
semaphore = dispatch_semaphore_create(0);
session = [NSURLSession sessionWithConfiguration: [NSURLSessionConfiguration defaultSessionConfiguration]
delegate: self
delegateQueue: nil];
NSMutableURLRequest* req = [NSMutableURLRequest requestWithURL: [NSURL URLWithString: url]
cachePolicy: NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval: 60.0];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest: req];
[dataTask resume];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}
- (void)connectWithCompletionHandler:(NSString *)url
{
semaphore = dispatch_semaphore_create(0);
session = [NSURLSession sharedSession];
NSMutableURLRequest* req = [NSMutableURLRequest requestWithURL: [NSURL URLWithString: url]
cachePolicy: NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval: 60.0];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:req completionHandler: ^(NSData *data, NSURLResponse *response, NSError *error){
if (!error)
dispatch_semaphore_signal(semaphore);
}];
[dataTask resume];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}
Running these tests repeatedly with a range of different network conditions, the connectWithCompletionHandler: method consistently outperforms connectWithDelegate:. On slow connections it can be as much as 6 times faster to return.
The full test code can be found here.
Running this test over 20 iterations on a 5Mb/s link with 50 ms latency yields an average connection time of 0.6 seconds for connectWithCompletionHandler: and 2 seconds for connectWithDelegate:
What accounts for the difference in response time between these two methods...?
Related
I work on a hybrid based Xcode application, processing requirement app request to many times to server, this is same time WebView content loading, same times HttpRequest with POST, any time after many requests, web will or httpRequest not more working, by HttpRequest return always null and by WebView content not more load (blank screen), for that I become any errors on console or by catch, if I remove app from device and install again, is all work perfectly again.
My HttpRequest:
NSString * stringURLComplete = `http://mydomain.c`
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:stringURLComplete]];
[req setValue:#"gzip" forHTTPHeaderField:#"Accept-Encoding"];
[req setHTTPBody:[strData dataUsingEncoding:NSUTF8StringEncoding]];
[req setHTTPMethod:#"POST"];
NSError * err = nil;
NSURLResponse * response = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:req returningResponse:&response error:&err];
NSDictionary *resulta = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
NSArray *list = [resulta objectForKey:#"data"];
return list;
In my application, I am performing POST using NSURLSession.
Steps followed:
Setting Header
Setting HTTPBody
Making POST request using NSURLSession.
The code is:
NSDictionary *parameters = #{ #"searchTerm": #"shampoo", #"sort": #"Most Reviewed" };
NSError *error = nil;
NSData *postData = [NSJSONSerialization dataWithJSONObject:parameters options:NSJSONWritingPrettyPrinted error:&error];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"SomeURL"]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:10.0];
[request setHTTPMethod:#"POST"];
[request setAllHTTPHeaderFields:headers];
request.HTTPBody = postData;
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
NSLog(#"%#", error);
} else {
NSLog(#"Pass");
}
}];
[dataTask resume];
Now in custom NSURLProtocol class:
(BOOL)canInitWithRequest:(NSURLRequest *)request {
if ([request.HTTPMethod isEqualToString:#"POST"]) {
//here request.HTTPMethod is coming nil
//Whereas my requirement is to get request.HTTPMethod which got request parameter.
return YES;
}
return NO;
}
Thanks in advance.
IIRC, body data objects get transparently converted into streaming-style bodies by the URL loading system before they reach you. If you need to read the data:
Open the HTTPBodyStream object
Read the body data from it
There is one caveat: the stream may not be rewindable, so don't pass that request object on to any other code that would need to access the body afterwards. Unfortunately, there is no mechanism for requesting a new body stream, either (see the README file from the CustomHTTPProtocol sample code project on Apple's website for other limitations).
I have a subclass of NSURLProtocol,
The NSURLConnection works well.
And [NSURLSession sharedSession] works well too.
But, [NSURLSession sessionWithConfiguration:configuration] not work,
I called the [NSURLProtocol registerClass:NSURLProtocolSubclass.class];
what's error?
NSString * stringUrl = #"https://www.apple.com/";
NSURL * url = [NSURL URLWithString:stringUrl];
NSMutableURLRequest * urlRequest = [NSMutableURLRequest requestWithURL:url];
urlRequest.HTTPMethod = #"GET";
NSURLSessionConfiguration * configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
configuration.timeoutIntervalForResource = 20.0;
//this can work, but I cann't edit.
//configuration.protocolClasses = #[NSURLProtocolSubclass.class];
//this can work.
NSURLSession * urlSession = [NSURLSession sharedSession];
//this not work.
//NSURLSession * urlSession = [NSURLSession sessionWithConfiguration:configuration];
//Task.
NSURLSessionDataTask * task = [urlSession dataTaskWithRequest:urlRequest completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSHTTPURLResponse * httpUrlResponse = (NSHTTPURLResponse *)response;
NSLog(#"---- session: %ld", (long)httpUrlResponse.statusCode);
}];
[task resume];
The registerClass method registers a protocol with NSURLConnection and with the shared session only. Other sessions copy the global set of protocols at the time the session is created, IIRC, unless you provide a custom array, in which case it copies that array.
Ether way, if you need to add protocols while your app is running, I'm pretty sure you have to create a new session each time unless you use the shared session.
So i have this method in my app that returns BOOL if and update is available for my apps content
- (BOOL)isUpdateAvailable{
NSData *dataResponse=[NSData dataWithContentsOfURL:[NSURL URLWithString:#"url that returns json object"] ];
if(dataResponse!=nil){
NSError *error;
dicUpdates = [NSJSONSerialization JSONObjectWithData:dataResponse options:NSJSONReadingMutableContainers error:&error];
}
if(dicUpdates.count > 0) isUpdateAvailable = YES;
else isUpdateAvailable = NO;
return isUpdateAvailable;
}
I need a synchronous request for this, cause the next view controller will be dependent on the server response. However sometimes it takes a long time for the server to respond or the internet is really slow, i need to set a time out to prevent the app from 'being frozen'.
I previously used NSUrlconnection to accomplish this task, but it has been deprecated.
Also, I tried using NSURLSession, (been using it also to download updates in the background thread), but i just can figure out if it can be used for a synchronous request.
Any idea how to deal with this? i just need a synchronous method that returns a BOOL. Best regards.
We have to use NSURLRequest in NSURLSession to set timeout interval.
Check below code:
- (BOOL)isUpdateAvailable{
NSURLSession *session = [NSURLSession sharedSession];
[[session dataTaskWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"url that returns json object"] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:4]//timeout
completionHandler:^(NSData *dataResponse,
NSURLResponse *response,
NSError *error) {
// handle response
if(dataResponse!=nil){
NSError *error;
dicUpdates = [NSJSONSerialization JSONObjectWithData:dataResponse options:NSJSONReadingMutableContainers error:&error];
}
if(dicUpdates.count > 0) isUpdateAvailable = YES;
else isUpdateAvailable = NO;
return isUpdateAvailable;
}] resume];
}
I have following code, it works fine in iOS7, but does not in iOS8.In iOS8, the app always use the cache even I have set NSURLRequestReloadIgnoringCacheData:
NSURL *url = [NSURL URLWithString:k_Chapter_URL];
NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:url];
NSURLSessionConfiguration *sessionConfig =
[NSURLSessionConfiguration defaultSessionConfiguration];
sessionConfig.requestCachePolicy = NSURLRequestReloadIgnoringCacheData;
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig];
[[session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
}] resume];
Is there anybody have similar situation with me?
I think this is a bug in iOS 8. I was able to use resetWithCompletionHandler to force the cache to clear.
[session resetWithCompletionHandler:^{
[[session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
}] resume];
}];