Using data from delegate after NSURLConnection has finished - objective-c

I've made an NSURLConnection, and made a seperate class to be used as a delegate, but I can't make use of the delegates data after the connection has finished. The data writes to console from within the delegate class, but not outside.
In the ServerCommunicationDelegate-class (the delegate), in method "connectionDidFinishLoading":
self.errorLog = [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding];
NSLog(self.errorLog); // Prints the data to console
In the class where the connection takes place:
ServerCommunicationDelegate *del = [[ServerCommunicationDelegate alloc] init];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:del];
NSLog(#"Errorlog %#", del.errorLog); // Returns null
"errorLog" is a property of "ServerCommunicationDelegate".
Is there something I misunderstood about the delegation-paradigm here, or is it maybe something else I've missed?
Either way, thanks in advance.

NSURLConnection works asynchronously. The line
NSLog(#"Errorlog %#", del.errorLog); // Returns null
is executed before the connection has finished loading (probably before it even started
loading).

Related

How can I pause the program until nsurlconnection complete?

I know that people have asked this but I have not found satisfactory answers. I have one method that I send all my URLRequests through. I return the response of the request as a string when the method completes. I have recently added ssl to my program. This means that I can no longer use a synchronous request because I need to take advantage of the didReceiveAuthenticationChallenge function as my credentials are currently self-signing. The program needs the response from the URL in order to continue so there is not harm in waiting for the response. However, I cannot seem to find a way to just hold the code up and continue once completed. I can alert the original function that called to request function but I would like the program to pick up right after that call. And it has unique code below such calls so I cannot specialize the connectionDidFinishLoading: function because each method who calls this is different.
How can I pause the program so I can return the nsdata from the connection to the methods that called it?
Here is some pseudo-code to show you what I mean:
- (void) login:(NSString *)username :(NSString *)password {
NSString *str = [NSString stringWithFormat:%#"%#:::%#",username,password];
NSURL *url = [NSURL urlWithString:#"https://blahblahblah"];
NSString *result = [self connectToUrl:str:url];
if ([result isEqualToString:#"valid"]) {
//this would be more complex in here
NSLog(#"hooray");
} else {
NSLog(#"bummer");
}
}
- (NSString *)connectToUrl:(NSURL *)url :(NSString *)str {
NSData *FileData = [str dataUsingEncoding: NSUTF8StringEncoding];
NSMutableData *data = [[NSMutableData alloc] initWithCapacity:100];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
[request setHTTPMethod:#"POST"];
//set up the rest of the request...
...
connection = [NSURLConnection connectionWithRequest:request delegate:self];
[connection start];
//WOULD LIKE TO PAUSE HERE UNTIL COMPLETE! THEN CONTINUE
// received data is assigned in didReceiveData: method
return [[[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding] autorelease];
}
But alas, I cannot do this because I cannot make the final line wait until the connection is complete... Please help me!
Very appreciative!
R
iOS and OS X and much of the Cocoa/Cocoa touch frameworks are built on an event model. You don't pause your app. That's not the proper approach. You need to start the connection and then move on. When the connection completes, you act on that event.
In other words, your login method can't sit and wait for the result. It should start the connection and return.
When you get the result of the connection you call some method to process the login result.
Making use of blocks can make things like this easier but there are other ways. You just need to stop thinking about such things in a linear fashion. Dealing with asynchronous processing requires a different approach.

NSURLConnection problems

Hello my problem is that the delegates methods of my NSURLConnection are not being called, I read other post with the same problem but I can't fix this.
#implementation firstViewController
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[sendData sendValues];
});
end
other class
#interface SendInformation //this class extend NSObject<NSURLConnectionDataDelegate>
-(void)sendValues{
.......
NSURLConnection *conn = [[NSURLConnection alloc] init];
(void)[conn initWithRequest:request delegate:self];
}
end
The connection is working (I have all the delegate methods inside SendInformation.m) the problem is I never get a response, I read that you need to run the connection in the main thread, but if I do that, the interface will be freeze until the connection is done also I want the NSURLConnection in a different class.
Your code for creating the connection is strange. You want:
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
And of course self must implement all of the appropriate NSURLConnectionDelegate methods.

How to handle different requests using connectionDidFinishLoading in the same delegate?

Whenever I do a curl call using the below code:
NSURL *url = [NSURL URLWithString:requestURL];
NSURLRequest *request = [NSURLRequest requestWithURL:url
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval:30];
if (connectionInProgress) {
[connectionInProgress cancel];
}
connectionInProgress = [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:YES];
connectionDidFinishLoading is my final destination where I can manipulate the response data and call my next methods to continue with the app . If I hard-code some specific tasks like
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:xmlData];
[parser setDelegate:self];
[parser parse];
[someLabel setText:parsedTextFromXMLData];
}
If I need to do another curl call to a different address, wouldn't someLabel setText always get re-set again? Is there a way to make this delegate function behave differently on each curl call? (btw, is connectionDidFinishLoading usually the right place to put the next step of codes?) If so then wouldn't it always get called again by the next curl call?
Have a look at this S.O. post for a recipe concerning NSURLConnection and multiple requests.The suggestion is doing something like this:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
if (connection == firstConnection) {
// do something
}
else if (connection == secondConnection) {
// do something else
}
}
EDIT: the idea here is that connectionDidFinishLoading is a method of your own delegate (so you write it). In the delegate, you store the address of each connection you create; then, when the connection comes back with the data, you tell which connection it is by comparing its address to the one you stored in the delegate. -END EDIT
Another option you have is using the ASIHTTPRequest framework, which offers a request-based (as opposed to connection-based) delegation mechanism, so each request has got a delegate object to handle the result; or, in other words, the delegate receives a reference to the request, so you can easily tell which request result you are handling.
ASIHTTPRequest offers a bunch of advantages over NSURLConnection. You can read about them in this S.O. post.
There're 2 options to do this:
you can implement a separate class, that will be responsible for handling NSURLConnection delegate stuff and create a separate instance for each request
you can use NSObject key-value methods on NSURLConnection instance for setting up some tag, that will be checked in connectionDidFinishLoading: method
For me, option 1 will be a better approach

Why release the NSURLConnection instance in this statement?

I read this in a book.
-(IBAction) updateTweets
{
tweetsView.text = #"";
[tweetsData release];
tweetsData = [[NSMutableData alloc] init];
NSURL *url = [NSURL URLWithString:#"http://twitter.com/statuses/public_timeline.xml" ];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL: url];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[connection release];
[request release];
[activityIndicator startAnimating];
}
In this statement,is that correct to release the "connection" instance at that time? After releasing it which means this NSURLConnection instance will be destroyed since it's reference count is 0 ,how are we going to make this connection operation work after release this instance? THANKS.
I thought we should release it in one of the following callback methods,right?
connectionDidFinishLoading:
connection:didFailWithError:
It's actually fine to release it there, once the connection is sent out via initWithRequest, the only thing that matters is that the delegate exists or I believe the response will just be silently lost.
From what I can tell, the only reason to not release it there is if you want to call [connection cancel] at some point in one of the delegate functions, in which case it would be best to do what you suggest and release it in BOTH connectionDidFinishLoading and didFailWithError since only one of them will be called (right?).
Edit: For a more thorough answer, NSURLConnection initWithRequest is an asynchronous request. So it actually spawns it's own thread (but calls the delegate functions on the thread that called initWithRequest). So basically, on the thread that calls initWithRequest you are actually done with the connection object and you can release it. All the while it's doing stuff on some other thread that you don't need to be concerned with.
Also I should note that if you do release it there, make sure you DON'T release it in the finish/fail methods, because it won't be a valid object.

What's wrong on following URLConnection?

See also:
Objective-C Asynchronous Web Request with Cookies
I spent a day writing this code and can anyone tell me what is wrong here?
WSHelper is inherited from NSObject, I even tried NSDocument and NSObjectController and everything..
-(void) loadUrl: (NSString*) urlStr{
url = [[NSURL alloc] initWithString:urlStr];
request = [NSURLRequest requestWithURL:url cachePolicy: NSURLRequestReloadIgnoringCacheData timeoutInterval: 60.0];
connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
if(connection)
{
receivedData = [[NSMutableData data] retain];
//[connection start];
}
else
{ display error etc... }
NSApplication * app = [NSApplication sharedApplication];
[app runModalForWindow: waitWindow];// <-- this is the problem...
}
-(void)connection: (NSURLConnection*)connection didReceiveData:(NSData*)data{
progressText = #"Receiving Data...";
[receivedData appendData:data];
}
-(void)connection: (NSURLConnection *)connection didFailWithError:(NSError *)error{
progressText = #"Error...";
NSAlert * alert = [[NSAlert alloc] init];
[alert setMessageText:[error localizedDescription]];
[alert runModal];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
progressText = #"Done...";
pData = [[NSData alloc] initWithData:receivedData];
[self hideWindow];
}
The code just wont do anything, it doesnt progress at all. I even tried it with/without startImmediately:YES but no luck !!!, this is executed in main window so even the thread and its run loop is running successfully.
I tried calling synchronous request, and it is working correctly !! But I need async solution.
I have added CoreServices.Framework in project, is there anything more I should be adding to the project? any compiler settings? Or do i have to initialize anything before I can use NSURLConnection?
Any solution to run NSURLConnection on different thread on its own NSRunLoop, Objective-C and MAC Development has no sample code anywhere in documentation that makes everything so difficult to code.
I also met the same problem that didn't get the delegate method called when using NSURLConnection in a Modal Window.
after some investigation, following code resolve it.
NSURLConnection* conn = [[NSURLConnection alloc] initWithRequest:requst delegate:self startImmediately:NO];
[conn scheduleRunLoop:[NSRunLoop currentRunLoop] forMode:NSModalPanelRunLoopMode];
[conn start];
However, when connectionDidFinishLoading called, [NSApp stopModal] doesn't work, need call [NSApp abortModal] instead.
Firstly you're making starting the connection too complicated. Change to:
connection = [[NSURLConnection alloc] initWithRequest:request delegate:self]
Remove [connection start]. Now:
Is your app definitely running the run loop normally? NSURLConnection requires this to work.
Are you able to perform a synchronous load of the URL request?
In the debugger, can you see that url is what you expect it to be? What is it?
Is it possible that you're deallocating WSHelper before any delegate messages are received? NSURLConnection is asynchoronous after all.
One does not need to do anything special to use NSURLConnection, it's a straightforward part of the Foundation framework. No special compiler settings required. No initialization before use. Nothing. Please don't start blindly trying stuff like bringing in CoreServices.Framework.
As sending the request synchronously works, there must be something wrong with your handling of the asynchronous aspect. It could be:
The runloop is not running in NSDefaultRunLoopMode so the connection is unable to schedule itself.
Some other part of your code is calling -cancel on the connection before it has a chance to load.
You are managing to deallocate the connection before it has a chance to load.
Real problem
Ah, in fact I've just realised what's going on. You are calling:
-[NSApp runModalForWindow:]
Read the description of what this method does. It's not running the run loop like NSURLConnection expects. I'd say that really, you don't want to be presenting a window quite like this while running a URL connection for it.
I'd also suggest that you implement the -connection:didReceiveResponse: delegate method too. You want to check here that the server is returning the expected status code.
You say that you're using this in a modal dialog? A modal dialog puts the run loop into a different mode. You should be able to get this to work by scheduling it to run in the modal dialog run loop mode, in addition to the normal run loop mode. Try adding this line of code after you allocate connection in loadURL:
[connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSModalPanelRunLoopMode];
Hope that helps.
How do you know it isn't doing anything? Are there any error or warning messages during the compile? Are any error messages showing up on console when the program is running?
Have you tries setting breakpoints in your code and following through what you expect to be happening?