getting http headers by NSURLConnection sendAsynchronousRequest - objective-c

How do I get http headers by using sendAsynchronousRequest? Notice that the target URL of the URLRequest only sets headers. When I use sendSynchronousRequest, I can get the http headers by using the NSURLHTTPResponse object. However, in sendAsynchronousRequest, there is only a NSURLResponse object. I have used the following cast strategy to convert NSURLResponse to NSURLHTTPResponse in sendAsynchronousRequest method. This however does not work, blockinkg the main thread without generating any error or exception.\
[NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
NSHTTPURLResponse *hResponse = (NSHTTPURLResponse*) response;
if ([hResponse respondsToSelector:#selector(allHeaderFields)]) {
NSDictionary *dictionary = [hResponse allHeaderFields];
_myHeader = [dictionary objectForKey:#"myHeader"];
}
}
If I don't use the above code, there is no blocking. I have also notice that the NSData returned by sendAsynchronousRequest hás zero length. Please help

Related

NSURLSession dataTaskWithRequest returns nil data

I am new to Mac application development and our existing Mac application contains the following line of code
[NSURLConnection sendSynchronousRequest:request returningResonse:response error:Error
A warning message getting displayed as
sendSynchronousRequest is deprecated in macOS 10.11
and suggesting to use [NSURLSession dataTaskWithRequest:completionHandler:]
I have implemented the following code changes to use NSURLSession as suggested but it is returning the value of data as "nil". Can you please suggest what needs to be done in order to get the required data in response?
__block NSError *WSerror;
__block NSURLResponse *WSresponse;
__block NSData *myData;
NSURLSession *session =[NSURLSession sharedSession];
[[session completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
myData = data;
WSresponse =response;
WSerror = error;
}] resume];
NSString *theXml = [[NSString alloc] initWithData:myData encoding:NSUTF8StringEncoding];
return theXml;
The completion handler is asynchronous. You have to do your work inside the completion handler.
The __block declarations are pointless.
NSURLSession *session =[NSURLSession sharedSession];
[[session completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
NSLog(#"%#", error);
} else {
NSString *theXml = [[NSString alloc] initWithData: data encoding:NSUTF8StringEncoding];
// do something with `theXml`
}
}] resume];

Code execution with sendAsynchronousRequest

A variable is being set to (null) due to the sendAsynchronousRequest not completing before the request is complete. See code:
main.m:
GlobalSettings *globalsettings = [[GlobalSettings alloc] init];
NSString *url = [globalsettings facebookLink];
NSLog(#"URL: %#", url);
So, inside GlobalSettings:
-(NSString *)facebookLink
{
__block NSString *strReturn;
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:#"http://urlEditedOut/"]];
__block NSDictionary *json;
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
json = [NSJSONSerialization JSONObjectWithData:data
options:0
error:nil];
strReturn = json[#"FB"];
}];
return strReturn;
}
So this works fine, has been tested inside the completion block. However back in main.m the variable url is being set to (null) due to (i assume) the async request still connecting / processing request.
How do you combat this so that the variable is saved as the correct value?
The url is set to null because the method returns immediately due to the asynchronous request. The way to avoid this is a delegate. make main the delegate of the GobalSettings and call the delegate method from the completion block. This SO-Post isnt an exact duplicate, but its close enough to get you started.
How to return the ouput if method is using NSURLConnection Asynchronous calls with blocks
Avt's answer is what i would suggest, but returning a block works, too.

Getting data out of the NSURLResponse completion block

It looks like I didn't get the concept of blocks completely yet...
In my code I have to get out the JSON data from the asychronous block to be returned to from the 'outer' method. I googled and found that if defining a variable with __block, the v̶i̶s̶i̶b̶i̶l̶i̶t̶y̶ _mutability_ of that variable is extended to the block.
But for some reason returned json object is nil.I wonder why?
- (NSMutableDictionary *)executeRequestUrlString:(NSString *)urlString
{
__block NSMutableDictionary *json = nil;
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPShouldHandleCookies:YES];
[request setHTTPMethod:#"GET"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-type"];
NSString *cookieString = [self.userDefaults objectForKey:SAVED_COOKIE];
[request addValue:cookieString forHTTPHeaderField:#"Cookie"];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue currentQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
NSLog(#"dataAsString %#", [NSString stringWithUTF8String:[data bytes]]);
NSError *error1;
NSMutableDictionary * innerJson = [NSJSONSerialization
JSONObjectWithData:data
options:kNilOptions
error:&error1];
json = innerJson;
}];
return json;
}
First, to answer your question:
But for some reason returned json object is nil. I wonder why?
The variable that you are returning has not been set at the time when you return it. You cannot harvest the results immediately after the sendAsynchronousRequest:queue:completionHandler: method has returned: the call has to finish the roundtrip before calling back your block and setting json variable.
Now a quick note on what to do about it: your method is attempting to convert an asynchronous call into a synchronous one. Try to keep it asynchronous if you can. Rather than expecting a method that returns a NSMutableDictionary*, make a method that takes a block of its own, and pass the dictionary to that block when the sendAsynchronousRequest: method completes:
- (void)executeRequestUrlString:(NSString *)urlString withBlock:(void (^)(NSDictionary *jsonData))block {
// Prepare for the call
...
// Make the call
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue currentQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
NSLog(#"dataAsString %#", [NSString stringWithUTF8String:[data bytes]]);
NSError *error1;
NSMutableDictionary * innerJson = [NSJSONSerialization
JSONObjectWithData:data options:kNilOptions error:&error1
];
block(innerJson); // Call back the block passed into your method
}];
}
When you call sendAsynchronousRequest:queue:completionHandler:, you've requested an asynchronous request. So it queues the request and the block and returns immediately. At some point in the future the request is made, and some point after that the completion block is run. But by that time, return json has long since run.
If you want to be able to return the data synchronously, then you must make a synchronous request. That will hang this thread until it completes, so it must not be the main thread.
Check the string when converting data coming from server using below code:
NSLog(#"dataAsString %#", [NSString stringWithUTF8String:[data bytes]]);
if the string is in a proper JSON format, ONLY then your JSON object will be correct.
Hope this hepls!!

Resolve redirect of NSURL?

I have a shortened URL (e.g. t.co/12345678). I would like to resolve the redirect without having to the load the entire page via NSURLConnection. Obviously, I can get it by sending an NSURLConnection and awaiting the response but this loads the entire page first. I am only looking for the redirect URL. Is this possible? If so, how?
=============
Edit: For posterity, here is the solution, as suggested by amleszk.
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:shortenedURL
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:15.0f];
[request setHTTPMethod:#"HEAD"];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
NSURL *resolvedURL = [httpResponse URL];
NSLog(#"%#", resolvedURL);
}];
same method as this: finding the download size of a URL (Content-Length)
but you want the [NSURLResponse statusCode] = 301/302/303 and the
Location header here: Getting "Location" header from NSHTTPURLResponse

ASIHTTPRequest and NSURLRequest different result

I send request to some url with ASIHTTPRequest, and always get strange response with different HTML body. If i make same request from browser or from NSURLRequest class, i get correct response with correct HTML body.
//Here is code of NSURLRequest. I'm getting correct response;
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLResponse *resp = nil;
NSError *err = nil;
NSData *responseData = [NSURLConnection sendSynchronousRequest: request returningResponse: &resp error: &err];
NSString *theString = [[NSString alloc] initWithData:responseData encoding:NSASCIIStringEncoding];
NSLog(#"%#",theString);
//Here is code of ASIHTTPRequest. I'm getting different response. The html body is shorter than original and different;
__block ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setCompletionBlock:^{
NSString *responseString = [request responseString];
}];
[request startAsynchronous];
I also tryed to send simple synchronous request with ASIHTTPRequest, but result the same. What it could be?
may be server have different logic for different request header