2

I'm trying to use the delegate methods of NSURLConnection. The following methods are currently not being called:

- (void)connectionDidFinishLoading:(NSURLConnection *)connection;

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;

- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
//I also want to be able to use self-signed https urls

- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace;
//I also want to be able to use self-signed https urls

I'm currently using a synchronous call but asynchronous seems better because after I complete the code base I'm going to implement it into an iPhone application and I can't have my ui freezing.

With the following methodresponseData = [NSMutableData dataWithData:[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]];

I get back the data I need but using asynchronous I seems I have to use the delegate methods to get data back. I tried to add the delegate by using @interface myClass : NSObject<NSURLConnectionDelegate>

I'm calling my method as follows:

-(void)grabData{
    NSArray* array = [NSArray arrayWithObjects:@"auth.login",@"user",@"pass", nil];
    NSData* packed_array = [array messagePack];

    NSURL* url = [NSURL URLWithString:@"https://192.168.1.115:3790/"];
    NSMutableURLRequest* request = [[[NSMutableURLRequest alloc]initWithURL:url]retain];
    [request setHTTPMethod:@"POST"];
    [request setValue:@"RPC Server" forHTTPHeaderField:@"Host"];
    [request setValue:@"binary/message-pack" forHTTPHeaderField:@"Content-Type"];
    [request setValue:[NSString stringWithFormat:@"%d",[packed_array length]] forHTTPHeaderField:@"Content-Length"];
    [request setHTTPBody:packed_array];

    //NSHTTPURLResponse *response = nil;
    NSError *error = nil;

    NSLog(@"connecting");
    NSURLConnection* connection = [[[NSURLConnection alloc]initWithRequest:request delegate:self]retain];
    if (connection) {
        NSLog(@"connection exists");
        self.responseData = [[NSMutableData data]retain];
    }
    else {
        NSLog(@"Connection doesn't exist?");
    }
    NSLog(@"response data: %@",[responseData messagePackParse]);
    NSLog(@"error: %@",error);
}

I've attempted the following:

NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[connection start];
erran
  • 1,300
  • 2
  • 15
  • 36
  • 1
    Did you set the `delegate` of the `NSURLConnection`? – Evan Mulawski Jun 20 '12 at 19:18
  • How do I set the delegate? When I used `[[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:YES];` it still didn't call the methods. (I NSLogged the name of the method inside every method.) – erran Jun 20 '12 at 19:22
  • 2
    So you already set the `delegate` to `self`. So that's not the problem. Is the URL valid? Try implementing `connection:didFailWithError:` and log the error details. – Evan Mulawski Jun 20 '12 at 19:24
  • I'll do that now, the host I set up for testing purposes (connecting to self-signed addresses). – erran Jun 20 '12 at 19:25
  • I would go with what @EvanMulawski said and add didFailWithError and set up a log in every delegate method and see if any gets called. I bet one of them will be called! – Ladislav Jun 20 '12 at 19:31
  • I've already logged in every method, do I have to call them manually or shouldn't they get called on `[connection start];`? – erran Jun 20 '12 at 19:35
  • How do I make my class implement the delegate? I thought adding did that for me. – erran Jun 20 '12 at 19:36

5 Answers5

9

I continued to search for related questions and found this answer most helpful. It lead me to using the following method. First I declared a BOOL property called finished in my interface and added the following method in my implementation which caused my delegate methods to be called.

while(!finished) {
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
Community
  • 1
  • 1
erran
  • 1,300
  • 2
  • 15
  • 36
  • Why don't you open up a question? I haven't encountered that problem and I'm no longer programming in Objective-C (I'm using RubyMotion now). – erran Apr 20 '13 at 17:51
2

Just a thought- any chance you're using Automatic Reference Counting and the object that is creating the NSURLConnection is being deallocated between the time you call [connection start] and when the delegate methods should be called (i.e., data has been received)?

Does anything maintain a reference to the object you're using to create the NSURLConnection?

Based on additional information, it does seem like this is a likely cause. You likely have code such as the following in your App Delegate (or another class you've created).

MyClass *object = [[MyClass alloc] init];
[object grabData];

This is great- it instantiates an object and then tells that object to grab the data in question. But, once grabData completes (and before the NSURLConnection returns data), object will be deallocated because nothing is holding on to it. To fix this:

  1. Create a property in your .h file (where you create the object) to hold onto your instance of MyClass.

    @property (strong) MyClass *object;

  2. Synthesize that property in your .m file

    @synthesize object;

  3. Rather than just creating your object, hold a reference to it:

    MyClass *myClass = [[MyClass alloc] init];

    [myClass grabData];

    self.object = myClass;

This will prevent your object from being deallocated by ARC.

When you're totally done with the object (your NSURLConnection has returned data), you can set self.object = nil to get rid of that reference.

Jeff Hellman
  • 2,117
  • 18
  • 17
  • How would I check if the object is being deallocated? (By object I'm assuming you mean my request?) – erran Jun 20 '12 at 20:13
  • I don't mean the request- I mean the actual object where you are creating the NSURLConnection. My guess is that you're creating an object (in code) that then creates an NSURLConnection. That original object is deallocated after the creation of the NSURLConnection but before the NSURLConnection returns data. – Jeff Hellman Jun 20 '12 at 20:27
  • `NSURLConnection* connection = [[NSURLConnection alloc]initWithRequest:request delegate:self];` is what I use to create the NSURLConnection. – erran Jun 20 '12 at 20:35
  • Yup. That looks just fine. But, that code is written inside a method declaration in some class you've written. What class is that? Have you instantiated an object of that class? If you don't maintain a reference to the object of the class that handles the creation of your NSURLConnection, you'll see this exact behavior. – Jeff Hellman Jun 20 '12 at 20:47
  • I have a class which has the 4 methods mentioned in my question and an additional void method. I call when I create an instance of my class in main. I added the method to my question. – erran Jun 20 '12 at 20:54
  • Great- that's what I expected. Someplace (perhaps in your App Delegate) you are instantiating (creating) an object of the class you created. If your App Delegate doesn't maintain a reference to the object you created, the delegate of NSURLConnection will fail. I've edited my answer to help a bit. – Jeff Hellman Jun 20 '12 at 21:12
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/12826/discussion-between-ipwnstuff-and-jeff-hellman) – erran Jun 20 '12 at 22:06
  • You had the right concept going on I figured out the problem though +1 for leading me in the right direction. – erran Jun 21 '12 at 21:36
1

You can use this:

NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest: request delegate:self startImmediately:NO];

[connection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];

[connection start];
spenibus
  • 4,339
  • 11
  • 26
  • 35
Mohamad Chami
  • 1,226
  • 14
  • 10
0

If you're having trouble you can do what I've done in the past and use synchronous requests like so:

NSHTTPURLResponse *response = nil;
NSError *error = nil;
[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]

This will output the data you need, all you need to do for it to not lock up your UI is use a block (GCD):

dispatch_queue_t downloadQueue = dispatch_queue_create("download queue", NULL);
dispatch_async(downloadQueue, ^{
  NSHTTPURLResponse *response = nil;
  NSError *error = nil;
  NSData *myData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]
  dispatch_async(dispatch_get_main_queue(), ^{
    // Handle your response data
  });
});
8vius
  • 5,786
  • 14
  • 74
  • 136
  • I attempted it this way as opposed to using just `responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];` found that above didn't execute any logging I attempted or me I trying to handle my data with. – erran Jun 20 '12 at 20:02
  • 1
    How so? I think you should really consider Jeff Hellman's proposition about the object being deallocated in that case. Check if the object doing the call exists when it does the call and is not null. – 8vius Jun 20 '12 at 20:05
  • The problem with this is that you cannot cancel a synchronous request. That's what I'm trying to work through at the moment as I'm getting similar issues with delegate messages not being received. – Quintin Willison Jun 22 '12 at 09:51
0

After establishing the connection just write below

[connection start]; your delegate method will be called.

Darshan Mothreja
  • 506
  • 4
  • 13