NSUrlConnectionDelegate not calling methods to load data - ios5

I have looked at NSURLConnectionDelegate connection:didReceiveData not working already, but there didn't seem to be any good result from that, so I am curious why I am not able to get any data.
I put in breakpoints in didReceiveResponse and didReceiveData.
It does print out "connection succeeded", so I know that the connection is started.
I am using ARC for memory management.
- (void)load {
request = [NSMutableURLRequest requestWithURL:myURL
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval:60];
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if (conn) {
[conn start];
NSLog(#"connection succeeded, %s", [myURL description]);
responseData = [NSMutableData data];
} else {
NSLog(#"connection failed");
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
responseData = [[NSMutableData alloc] init];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[responseData appendData:data];
}
UPDATE:
To see how I test this look at Asynchronous unit test not being called by SenTestCase.
I did implement the two methods mentioned by jonkroll, in his answer, I just didn't show them, but, they also aren't being called.
I had added [conn start] only because it wasn't working, and I was hoping that may solve it, but no such luck.

When you declare your connection like this:
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
You are creating a local pointer. When your method completes, since it was the last strong reference to the NSURLConnection, ARC releases it. You need to use a strong ivar (and/or) property to hold a strong reference to the NSURLConnection you create.
Edit
Here is basic sample of code that I tested in a sample project. Give it a run. Verbose logging helps.
#implementation <#Your class here#> {
// With ARC ivars are strong by default
NSMutableData *_downloadedData;
NSURLConnection *_connection;
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
NSHTTPURLResponse *realResponse = (NSHTTPURLResponse *)response;
if (realResponse.statusCode == 200){
// Really any 2** but for example
_downloadedData = [[NSMutableData alloc] init];
NSLog(#"Good response");
} else {
NSLog(#"Bad response = %i",realResponse.statusCode);
}
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
if (connection == _connection){
[_downloadedData appendData:data];
NSLog(#"Getting data...");
}
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
if (connection == _connection){
_connection = nil;
NSLog(#"We're done, inform the UI or the delegates");
}
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
_connection = nil;
NSLog(#"Oh no! Error:%#",error.localizedDescription);
}
- (void)load {
NSURL *url = [NSURL URLWithString:#"http://www.google.com/"];
NSURLRequest *request = [NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval:60];
// Assign strong pointer to new connection
_connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
NSLog(#"Connection was initialized? = %#",(!!_connection)?#"YES":#"NO");
}
#end

The NSURLConnection method initWithRequest starts an asynchronous request for data from a url. Because the request is done asynchronously you can't expect to work with the response in the same method in which the request is invoked. Instead you need to do so in the NSURLConnection's delegate callback methods. You have already implemented didReceiveResponse: and didReceiveData:, but there are a couple others that will be useful to you.
If you want to look at the contents of the response you should do so in connectionDidFinishLoading:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// response is complete, do something with the data
NSLog(#"%#", responseData);
}
The fact that your code prints out "connection succeeded" doesn't really mean that the request was successful, only that the NSURLConnection object was created successfully. To test whether there was a problem with the connection you can implement the delegate method connection:didFailWithError:
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"Connection failed! Error - %# %#",
[error localizedDescription],
[[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
}
Also there is no need to call [conn start]. The request will be started automatically when you call initWithRequest:
I suggest reading Apple's documentation on Using NSURLConnection for more details.

Related

How to use more than one delegate in iphone?

I am using below code to use json but i need more url connection in same page, how to achive it, thanks in advance
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[responseData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[responseData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog([NSString stringWithFormat:#"Connection failed: %#", [error description]]);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[connection release];
//do something with the json that comes back ... (the fun part)
}
- (void)viewDidLoad
{
[self searchForStuff:#"iPhone"];
}
-(void)searchForStuff:(NSString *)text
{
responseData = [[NSMutableData data] retain];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.whatever.com/json"]];
[[NSURLConnection alloc] initWithRequest:request delegate:self];
}
I am using php for web access
You could use instance variables to keep pointers to the connections. Then in the delegate callbacks, check for pointer equality to check which connection you're dealing with.
As NSValue conforms to NSCopying I use it to wrap the pointer to the connection, and use this as the key to access relevant data from a NSMutableDictionary. For example you might do something like the following:
-(void)searchForStuff:(NSString *)text withTarget:(id)target selector:(SEL)selector {
responseData = [[NSMutableData data] retain];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.whatever.com/json"]];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:target,#"target",NSStringFromSelector(selector),#"selector",nil];
NSURLConnection *c = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[myMutableDictionary setObject:options forKey:[NSValue valueWithPointer:c]];
[c release];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSValue *p = [NSValue valueWithPointer:connection];
NSDictionary *options = [myMutableDictionary objectForKey:p];
if (options) {
id target = [options objectForKey:#"target"];
SEL selector = NSSelectorFromString([options objectForKey:#"selector"]);
if (target && selector && [target respondsToSelector:selector]) {
[target performSelector:selector withObject:responseData];
}
}
}
Don't do any of that.
Instead use the brilliant ASIHTTPRequest library, which makes everything much simpler and better. Literally, since I discovered ASI a couple years ago, I haven't written a single NSURLConnection, not one.
ASI's block interface lets you configure a request object with its handler code before firing it, and does away with any need for delegation.
__block ASIHTTPRequest *r = [ASIHTTPRequest requestWithUrl:myNSURLObject];
[r setCompletionBlock:^{
NSLog([r responseString]); //for instance
}];
[r startAsynchronous];
If blocks scare you, you can also point a particular request at a particular method, so different request types can be handled separately:
- (void) viewDidLoad { //or wherever
ASIHTTPRequest *r = [ASIHTTPRequest requestWithUrl:myFirstURL];
r.delegate = self;
[r setDidFinishSelector:#selector(requestDone:)];
[r startAsynchronous];
}
// then later on...
- (void)requestDone:(ASIHTTPRequest *)request
{
NSString *response = [request responseString];
}
You don't actually need more than one delegate. You need more than one NSURLConnection and you can test to see which one is calling the delegate method.
For example. Assuming the following instance variable (or properties):
NSURLConnection *connectionA;
NSURLConnection *connectionB;
NSMutableData *dataA;
NSMutalbeData *dataB;
First you instantiate each NSURLConnection variable
-(void)searchA:(NSString *)text
{
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:#"http://www.a.com/%#", text]]];
connectionA = [[NSURLConnection alloc] initWithRequest:request delegate:self];
}
-(void)searchB:(NSString *)text
{
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:#"http://www.b.com/%#", text]]];
connectionB = [[NSURLConnection alloc] initWithRequest:request delegate:self];
}
Then you can test to see which connection is calling the delegate method and customize the implementation based on the connection
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
if (connection == connectionA) {
[dataA appendData:data];
}
else if (connection == connectionB) {
[dataB appendData:data];
}
}
You'll need to do this for each delegate method.

Multiple NSURLCoonections in same View using iphone sdk

Can any one of you post the code snippet, example tutorial on handling the multiple
NSURLConnections from the same viewController using cocoa Touch framework....
Thanks for all your future help.....
I handled multiple NSUrlConnections using a NSMutableDictionary which keeps track of which instance of NSMutableData a particular NSURLConnection should save its result to.
At the beginning of my class I define:
NSMutableDictionary *dataDictionary;
Then in my loadData method, I have:
// Create the request
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:currentCam]
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval:30];
// create the connection with the request
// and start loading the data
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
NSMutableData *receivedData = receivedData = [[NSMutableData alloc] init];
//keep track of this connection by adding it to subViewDictionary and dataDictionary with appropriate objects.
[dataDictionary setObject:receivedData forKey:[theConnection description]];
}
else {
NSLog(#"ERROR DOWNLOADING WITH NSURLCONNECTION");
}
[theConnection release];
I use [theConnection description] as my key and an instance of MSMutableData as the object in my dictionary, so later I can look up which instance goes with a particular connection. If you fail to do this you can have issues with data corruption (multiple connections can all save their data to the same variable).
Then, I define the following NSURlConnection delegate methods:
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSMutableData *)data
{
//look up in dictionary to find out which recievedData instance to use.
NSMutableData *theReceivedData = [dataDictionary objectForKey:[connection description]];
// Append the new data to receivedData.
[theReceivedData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
//Lookup in the dictionary to find the correct instance of recievedData to put image into.
NSMutableData *theReceivedData = [dataDictionary objectForKey:[connection description]];
[theReceivedData setLength:0];
[activityIndicator stopAnimating];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
// inform the user that there was an error
NSLog(#"Connection failed! Error - localizedDescription:%# NSURLErrorFailingURLStringErrorKey:%#",
[error localizedDescription],
[[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
NSMutableData *theReceivedData = [dataDictionary objectForKey:[connection description]];
[theReceivedData release];
//remove keys for this connection since it did not load.
[dataDictionary removeObjectForKey:[connection description]];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
//look up correct instance of recievedData in teh dictionary for this connection
NSMutableData *theReceivedData = [dataDictionary objectForKey:[connection description]];
NSLog(#"Succeeded! Received %d bytes of data",[theReceivedData length]);
//---do stuff with data here//
[theReceivedData release];
}
There is a good tutorial on NSURlConnection here for a single connection. My code is based on that with the addition of the NSMutableDictionary to keep track of each NSUrlConnection and each NSMutableData instance.
I hope that makes sense and is helpful!
See this link for the answer. It wraps nsurlconnection with asihttp, which makes your life much easier.
Does ASIHTTP support multi threads?

iPhone NSURLConnection - delegates not working

I am trying to get an NSURLConnection to work in my app. I have followed apple's code almost exactly but it doesn't seem to work. The NSURLConnection is inside of a method called, downloadSave. This method runs properly through the end, and my log indicates, "Connection Exists" - however nothing happens after that as if none of the delegate methods get called.
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSString *tempString = [[NSString alloc]initWithFormat:#"http://www.myWebsite.com/%#.jpg",chartFileName];
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:tempString]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:10.0];
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
mutableData = [[NSMutableData data] retain];
self.image = nil;
NSLog(#"connection exists");
} else {
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Connection Error" message:#"There was an error contacting the servers. Please try again." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
[alert release];
[activityIndicator stopAnimating];
}
[pool drain];
[pool release];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSLog(#"got to connection did receive response");
[mutableData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[mutableData appendData:data];
NSLog(#"got some data were at %i",mutableData.length);
}
- (void)connection:(NSURLConnection *)connection
didFailWithError:(NSError *)error
{
[connection release];
// receivedData is declared as a method instance elsewhere
self.mutableData = nil;
NSLog(#"Connection failed! Error - %# %#",
[error localizedDescription],
[[error userInfo] objectForKey:NSErrorFailingURLStringKey]);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"Succeeded! Received %d bytes of data",[mutableData length]);
//more code follows to display the downloaded image
}
The only thing that appears in the log is: "Connection Exists"
I can only guess by your code that downloadSave in called in a separate thread as you have an NSAutoReleasePool (not saying thats what your doing but its likely). NSURLConnection can only respond to the delegate methods in the main thread when it is initialised in the main thread.
As NSURLConnection already is a threaded delegate call you shouldn't need to create it in thread. If you need to thread it for some reason you should be able to use
NSError *error;
NSURLResponse *response;
NSData *connectionData = [NSURLConnection sendSynchronousRequest:theRequest returningResponse:&response error:&error];
And that should return the data to the child thread.

Get URL File Size before parsing

Me again! I am using NSURL to get a file then parse it. I have been looking for a couple of hours now on a progressbar i am trying to implement in my app. I know I first need to get the file size, then keep updating how much data i've downloaded as I continue to pull. I have seen the example using "ASIHTTPRequest" but is there a way to do it with what I already have?
Here is where I start the download.
-(void)parseNewData {
//start network activity spinner and release controller when done
parserDone = NO;
[root downloadIcon];
//create pool to avoid memory leak
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// get the XML path and start parsing
NSURL *pathURL = [NSURL URLWithString:#"http://www.mysite.com/myfile.xml"];
NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:pathURL];
[parser setDelegate:self];
[parser parse];
//drain pool
[pool drain];
[pool release];
}
Can someone point me in the right direction on how to get file size, then how to update how much I've downloaded. Thanks in advance!
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[webData setLength: 0];
CGFloat size = [[NSString stringWithFormat:#"%lli",[response expectedContentLength]] floatValue];
NSLog(#"Size : %f",size);
}
Above code will give you total size
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[webData appendData:data];
totalDownloadedData += [data length]; // global integer
NSLog(#"Status : %d/%f",totalDownloadedData,size);
}
Above code will show you current status of downloading
You need to use NSURLConnection if you want to get the file size and the progress. You get delegate methods which you can use to monitor progress. The didSendBodyData: delegate method tells you how much data there is in bytes. The connectionDidFinishLoading is where you uget receivedData to use in your NSXMLParser code.
NSURLRequest *theRequest = [NSURLRequest requestWithURL:URL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10.0];
NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
receivedData = [[NSMutableData data] retain];
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
// This method is called when the server has determined that it
// has enough information to create the NSURLResponse.
// It can be called multiple times, for example in the case of a
// redirect, so each time we reset the data.
// receivedData is an instance variable declared elsewhere.
[receivedData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
// release the connection, and the data object
// inform the user
NSLog(#"Connection failed! Error - %# %#",
[error localizedDescription],
[[error userInfo] objectForKey:NSErrorFailingURLStringKey]);
[connection release];
// receivedData is declared as a method instance elsewhere
[receivedData release];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
// Append the new data to receivedData.
// receivedData is an instance variable declared elsewhere.
[receivedData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite{
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{

"-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data" not called

Have a look to this code snippet:-
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[webData setLength: 0];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSLog(#"Recieving Data...");
[webData appendData:data];
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"ERROR with theConenction");
[connection release];
[webData release];
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"DONE. Received Bytes: %d", [webData length]);
NSLog(theXML);
}
I am calling a SOAP web service.There are no errors or warnings displayed in my code.
When I hit the web service through safari it works fine. But the problem arises when I try
hit it through my codes.
Everything works fine but the connection:didRecieveData does not gets called.
Thus, I get no data in the webData variable. This webData is a NSMutableData object.
The problem seems to be silly but any one with any answers ....
Thank You All.
I suspect you are having a memory management issue. I could be mistaken on this, but I believe that even:
NSURLConnection* connection=[[NSURLConnection alloc] initWithRequest:request delegate:self];
won't work, because connection will be released at the end of the containing method, when connection goes out of scope. Make sure NSURLConnection *connection and NSMutableData *data are declared as member variables where ever you are doing this, and that you alloc and init them appropriately. My code usually looks like:
NSURLRequest *request = [NSURLRequest requestWithURL:url
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval:30.0];
// cancel any old connection
if(connection) {
[connection cancel];
[connection release];
}
// create new connection and begin loading data
connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if(connection) {
// if the connection was created correctly, release old data (if any), and alloc new
[data release];
data = [[NSMutableData data] retain];
}
Also, release the connection and data in dealloc. For good measure, release and set them to nil at the very end of didFailWithError and didFinishLoading:
[connection release];
connection = nil;
[data release];
data = nil;
Good luck; I've done this a million times, let me know if you cannot get it working.
You don't happen to be calling the NSConnection in a thread do you? If you are then what's happening is that the thread is terminating before NSConnection and its delegates have finished so it'll just bomb out without an error.
A workaround for this is in this thread
You're not getting any error messages in didFailWithError either? Kind of a silly suggestion, but are you sure you're setting the proper NSURLConnection delegate?
NSURLConnection* connection=[[NSURLConnection alloc] initWithRequest:request delegate:self];
Sometimes it's something small like that.
Another idea is to drop in a toolkit like ASIHTTPRequest and see if it works going through them.
There also could be problems, if are trying to start NSURLConnection from another Thread.
Please call method [connection start] on main thread, if you have not customized Run Loop for it.