How does one use NSURLConnection delegate callbacks when using the
+ (void)sendAsynchronousRequest:(NSURLRequest *)request
queue:(NSOperationQueue*) queue
completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))
method?
I would like to be able to access the caching delegate callback on the queue handling the completion block.
You don't. You need to use the NSURLConnection method, initWithRequest:delegate:, instead of sendAsynchronousRequest, to use the delegate call back methods.
Just use it like this
NSURL *url = [NSURL URLWithString:kURL];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL : url
cachePolicy : NSURLRequestReloadIgnoringCacheData
timeoutInterval : 30];
NSString *params = [NSString stringWithFormat:#"param=%d",digits];
[request setHTTPMethod:#"POST"];
[request setHTTPBody:[params dataUsingEncoding:NSUTF8StringEncoding]];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *taxiData, NSError *error) {
//Snippet - Do sth. (block)
}
Hope this help.
EDIT: Sorry, I didn't read your question clearly. +sendAsynchronousRequest did not require delegates method.
EDIT2: or, maybe, this will help you
In order to use delegate methods with NSURLConnection you need to instantiate a NSURLConnection variable. Since
+ (void)sendAsynchronousRequest:(NSURLRequest *)request
queue:(NSOperationQueue*) queue
completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))
is a superclass method you can't use it.
Related
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.
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!!
I'm performing asynchronous request with ASI HTTP Request and I want to update Textbox with new information from the request, so I'm updating it from callback function.
Here is my code so far:
Second Class
- (void)Login {
NSLog(#"Login");
NSURL *url = [NSURL URLWithString:#"http://ts5.travian.sk/login.php"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDelegate:self];
[request setDidFinishSelector:#selector(loginRequestFinished:)];
[request setDidFailSelector:#selector(loginRequestFailed:)];
[request startAsynchronous];
}
- (void)loginRequestFinished:(ASIHTTPRequest *)request
{
NSLog(#"Completed!");
NSString *response = [request responseString];
AppController *ac = [AppController getInstance];
[ac.textbox performSelectorOnMainThread:#selector(setStringValue:) withObject:response waitUntilDone:NO];
}
AppController is a main class. Setting text from there is working. But this code isnt doing anything. It just wrote 2 log lines to debug window.
Am I missing something?
I have a class, that gets some data using NSURLConnection. It's method getData creates a request to a server and when some data recieved, method connection:didRecieveData: updates some properties.
- (void)getData
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:dataURL];
NSURLConnection *connectionWithRequest = [NSURLConnection connectionWithRequest:request delegate:self];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// Processing data
dataProperty = processedData;
}
The problem is, when I create an instance of this class and call method getData, I can't immediately get object's properties, because data is not received yet. I've read Apple reference about delegates and protocols, but I don't understand how to implement delegate method for this class, that would work like connection:didRecieveData: for NSURLConnection.
Can you explain me how to do this? I would be very glad, if you just post a link to an example. Thank you.
I don't understand how to implement delegate method for this class, that would work like connection:didRecieveData: for NSURLConnection.
The same way NSURLConnection does:
Give this object a property named delegate.
Set that property to another object.
In connectionDidReceiveData:, send a message to the delegate.
In the delegate, implement the method that the other object will be calling.
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"YOUR API URL"]];
NSString *email = #"username#gmail.com";
NSString *password = #"123456";
NSString *deviceToken = #"simulator";
NSString *deviceType = #"1";
NSString *post = [NSString stringWithFormat:#"email=%#&password=%#&deviceToken=%#&deviceType=%#",email,password,deviceToken,deviceType];
NSData *requestBodyData = [post dataUsingEncoding:NSUTF8StringEncoding];
request.HTTPMethod = #"POST";
request.HTTPBody = requestBodyData;
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse response, NSData responseData, NSError *error)
{
NSLog(#"%#",responseData);
NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableContainers error:nil];
NSLog(#"%#",dic[#"data"]);
}];
As I understand methods named *WithContentsOfURL: like [NSData dataWithContentsOfURL:] are synchronous.
So if I want to download from 3 URLs asynchronously using *WithContentsOfURL: methods I have to put them in a GCD dispatch like:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *dataOne = [NSData dataWithContentsOfURL:dataOne];
NSData *dataTwo = [NSData dataWithContentsOfURL:dataTwo];
NSData *dataThree = [NSData dataWithContentsOfURL:dataThree];
});
Is NSURLConnection using GCD "behind the scenes"? Would this be (somewhat) equivalent to the below methods in terms of asynchronous download:
NSURLRequest *myRequestOne = [NSURLRequest requestWithURL:[NSURL URLWithString:URLOne] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
NSURLConnection *myConnectionOne = [[NSURLConnection alloc] initWithRequest:myRequestOne delegate:self];
NSURLRequest *myRequestTwo = [NSURLRequest requestWithURL:[NSURL URLWithString:URLTwo] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
NSURLConnection *myConnectionThree = [[NSURLConnection alloc] initWithRequest:myRequestTwo delegate:self];
NSURLRequest *myRequestThree = [NSURLRequest requestWithURL:[NSURL URLWithString:URLThree] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
NSURLConnection *myConnectionThree = [[NSURLConnection alloc] initWithRequest:myRequestThree delegate:self];
Also what would happen if I would put a NSURLConnection inside a dispatch_async ?
They're not really equivalent, since using NSURLConnectionDelegate allows you to react to things like request failure, authentication challenge etc.
The first example you give using GCD would work for valid URLs, but for anything else will result in no data being returned. Do as Eugene suggests and use ASIHTTPRequest - it's much easier.
Just use ASIHTTPRequest, asynchronous requests are implemented there you'll just have to do something like this:
ASIHTTPRequest *myRequestOne = [ASIHTTPRequest requestWithURL:URLOne];
[myRequestOne setCompletionBlock:^ {
// do something with [request responseData];
}];
[myRequestOne startAsynchronous];