What's wrong on following URLConnection? - objective-c

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?

Related

Using data from delegate after NSURLConnection has finished

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).

Checking for NSRunLoop and using appropriate NSURLConnection method

I'm working on a personal project and ran into the asynchronous nature of NSURLConnection last night. I'm building a library that is going to interface with a restful api. I'm anticipating reusing this library, in both Foundation command line tools as well as Cocoa applications.
Is there a way I can either check if the runloop is available to call the synchronous method if it is, and send a synchronous request if it is not (in the event of being used in a command line tool).
Alternately, is there a way to always use the asynchronous method but force the application to not exit until the async request has finished?
I noticed this but I'd rather not have to put the call to run outside of the library.
Thanks for any help
Alternately, is there a way to always use the asynchronous method but force the application to not exit until the async request has finished?
Easy as pie:
int main(int argc, char *argv[])
{
// Create request, delegate object, etc.
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request
delegate:delegate
startImmediately:YES];
CFRunLoopRun();
// ...
}
I use CFRunLoopRun() here because it's possible to stop it later, when the delegate has determined that the connection is done:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// This returns control to wherever you called
// CFRunLoopRun() from.
CFRunLoopStop(CFRunLoopGetCurrent());
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(#"Error: %#", error);
CFRunLoopStop(CFRunLoopGetCurrent());
}
The other option is to use -[NSRunLoop runUntilDate:] in a while loop, and have the delegate set a "stop" flag:
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request
delegate:delegate
startImmediately:YES];
while( ![delegate connectionHasFinished] ){
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1];
}
I suppose you can check if the program is linked against AppKit by seeing if NSClassFromString(#"NSApplication") returns non-Nil. Then, you can check if you're on the main thread using +[NSThread isMainThread].
However, it's poor design for the library to try to "force" the app not to exit for whatever reason. Just establish that the library requires the cooperation of the app and provide a cleanup-and-finish routine for it to call. Possibly also a is-there-something-in-progress? function.

Cancel NSURLConnection Objective-C

I have a tableviewapplication, wich when the user select one view it needs to parse some XML to display information.But sometimes the XML is not finished downloading and the user can press the button to select the other view,generating a crash.I think i need to cancel the connection or something to dont cause any conflitct with the new connection,but i dont know exactly how,it suppose to be in ViewWillDisappear correct?
Heres how i start the connection on ViewDidAppear:
NSMutableURLRequest * req = [[NSMutableURLRequest alloc]initWithURL:[NSURL URLWithString:#"http://Adress"]
cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:20.0f];
conn = [NSURLConnection connectionWithRequest:req delegate:self];
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
if(conn)
{
receivedData = [[NSMutableData alloc]init];
[DSBezelActivityView newActivityViewForView:self.view withLabel:#"Loading..."];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
if(conn) [conn cancel];
}
You can call NSURLConnection's cancel method and it will prevent your connections delegate from being called with any more data. You could do this in viewWillDisappear if that's when it makes sense given how your app works.
Absolutely you can cancel NSURLConnection.
But you should be careful in cancelling it too.
If you decide to cancel it in viewWillDisappear,
then
You should not do,
autorelease
and also you should not,
release it anywhere.
Here below the brief explaination:
Do cancel your NSURLConnection delegate in viewWillDisappear
[nsurlconnection cancel];
and also you should release it here not anywhere,
[nsurlconnection release];
If you release the connection in somewhere like after your xml response received then,
It will call the viewWillDisappear method anyway,
here you are cancelling it , then it will lead your app to crash.
"deallocated objects will not be cancelled"
.
And also another situation will occurs while cancelling,
If user comes and immediately navigates other view first time, your nsurlconnection will be cancelled in viewWillDisappear method.
Again the user comes to the view and escapes immediately before your nsurlconnection initialized or allocated, also your app will be crashed because,
"deallocated objects will not be cancelled".
So, check your connection != nil before you cancel it
and also don't forgot to do
nsurlconnection = nil;
in the same time.
So that you can avoid the immediate calls [nsurlconnection cancel] crashes.
SO Finally , in your viewWillDisappear method you have to do is,
Need to check nsurlconnection != nil
Need to cancel it
Need to allocate null to your nsurlconnection
Need to release it in the same method.
Sample code will be like the following,
- (void) viewWillDisappear:(BOOL)animated
{
if (nsurlconnection != nil)
{
[nsurlconnection cancel];
[nsurlconnection release];
nsurlconnection = nil;
}
}
Hope it's helpful... Happy coding ...

runModalForWindow throttles http requests

I have url connection, which normally works fine
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request
delegate:delegate];
But when I create a modal window, no request ever receives response:
[NSApp runModalForWindow:window];
If I comment this line out, thus creating a 'standard' window, everything works.
I tried implementing all methods from NSURLConnectionDelegate, not a single of them called.
I suspect this is something about 'run loops', but have little experience in this area. Does anybody have experience in this?
Thank you
If you're targeting 10.5+, you can tell the NSURLConnection to also run in NSModalPanelRunLoopMode (the mode your current thread's runloop would be in while presenting a modal view) via
-(void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode
where aRunLoop would probably be [NSRunLoop currentRunLoop] and the mode would be NSModalPanelRunLoopMode. More info in the NSURLConnection doc.
If you're supporting earlier OSs, you may have to get creative (i.e. with multithreading). Good discussion of this issue pre-10.5 here.
I haven't bumped into the situation you're having, but I suggest spawning and starting a connection in background thread.
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 currentLoop] forMode:NSModalPanelRunLoopMode];
[conn start];
However, when connectionDidFinishLoading called, [NSApp stopModal] doesn't work, need call [NSApp abortModal] instead.

Cocoa: NSURLConnection not attempting an HTTP Request

I've had significant success with NSURL, NSURL[Mutable]Request, NSURLConnection with my iPhone applications. When trying to compile a stand alone Cocoa application, 10 line program to make a simple HTTP request, there are zero compiler errors or warnings. The program compiles fine, yet the HTTP Request is never made to my web server (I'm running a tcpdump and watching Apache logs in parallel). When I run very similar code in an iPhone app, essentially copy/pasted as evil as that is, all works golden.
I kept the code for the 'obj' declaration in the delegate to NSURLConnection out of this code snippet for the sake of simplicity. I'm also passing the following to gcc:
gcc -o foo foo.m -lobjc -framework cocoa
Thanks for any insight.
#import <Cocoa/Cocoa.h>
int main (int argc, char *argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString * urlstr = #"http://tmp/test.php";
[NSApplication sharedApplication];
NSObject *obj = [[NSObject alloc] init];
NSURL *url = [NSURL URLWithString: urlstr];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
if([request isKindOfClass:[NSMutableURLRequest class]])
NSLog(#"request is of type NSMutableURLRequest");
[request setHTTPMethod:#"GET"];
[request setCachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData];
NSURLConnection *connection = [[NSURLConnection alloc]
initWithRequest:request
delegate:obj
startImmediately:YES];
if(connection)
NSLog(#"We do have a connection.");
[pool release];
return 0;
}
The other poster pretty much answered this for you, but I thought I would just add a few things.
First, you don't really need to link to Cocoa for this, just linking to the Foundation framework is okay. Also, since you don't need a connection to the Window Server, you can get rid of the [NSApplication sharedApplicaiton] call. If you want just a simple, console test application to start with, use what you have now and add this before your [pool realease] call:
[[NSRunLoop currentRunLoop] run];
Please note, however, that this will block and may actually never return. Before calling this, you can add a timer if you want your code to actually do something in the background :) See the documentation on NSRunLoop for more ways to use this.
NSURLConnection is an asynchronous API that relies upon NSRunLoop. Your posted code never creates a run loop for the connection to run in. Therefore, I presume Cocoa is unable to create the connection and so returns nil. Things to look into:
1) Anything in the console? Is NSURLConnection throwing an exception or logging an error?
2) What happens if you use the synchronous API instead? +[NSURLConnection sendSynchronousRequest:returningResponse:error:]
3) What is the point of this code? Cocoa is not designed for running directly from a main() function yourself. Is there a particular reason why you are not using the Xcode-provided application templates that will take care of setting up run loop, autorelease pool etc.?