2

A short explanation what I want to do: I'm using NSURLConnection to connect to a SSL webpage which is my API. The servers certificate is a self signed one so you have to accept it, for example in a web browser. I've found a solution on Stack Overflow how to do the trick (How to use NSURLConnection to connect with SSL for an untrusted cert?)

So I've added the NSURLConnection delegate to use methods like "didReceiveAuthenticationChallenge". As a result of that I cannot use this:

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

because there is no possibility to use the delegate functions in this case. My question is the following: I need a function which looks like this:

- (NSDictionary *)getData :  (NSArray *)parameter {
    [...|
    NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; 
    [...]
    return myDictionary; 
}

how can I return a NSDictionary by using this? As far as you know the delegate function of NSURLConnection are called now and the response isn't available at this point. The problem is that the view controller depends on this response so I need to return the dictionary directly... Does anybody know a solution for this? What about a callback function?

Community
  • 1
  • 1
Tobias Bambullis
  • 736
  • 5
  • 17
  • 45

2 Answers2

1

okay, I've found a solution for that. A very good thing is to use blocks in objective-c.

First of all you have to add some methods to NSURLRequest and NSURL:

@implementation NSURLRequest (URLFetcher)

- (void)fetchDataWithResponseBlock:(void (^)(FetchResponse *response))block {
    FetchResponse *response = [[FetchResponse alloc] initWithBlock:block];
    [[NSURLConnection connectionWithRequest:self delegate:response] start];
    [response release];
}

@end

@implementation NSURL (URLFetcher)

- (void)fetchDataWithResponseBlock:(void (^)(FetchResponse *response))block {
    [[NSURLRequest requestWithURL:self] fetchDataWithResponseBlock:block];
}

@end

And than just implement the follwing class:

@implementation FetchResponse

- (id)initWithBlock:(void(^)(FetchResponse *response))block {
    if ((self = [super init])) {
        _block = [block copy];
    }
    return self;
}

- (NSData *)data {
    return _data;
}

- (NSURLResponse *)response {
    return _response;
}

- (NSError *)error {
    return _error;
}

- (NSInteger)statusCode {

    if ([_response isKindOfClass:[NSHTTPURLResponse class]]) return [(NSHTTPURLResponse *)_response statusCode];

    return 0;
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {

    _response = response; 

}

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

    if (!_data) _data = [[NSMutableData alloc] init];

    [_data appendData:data];

}

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

    _block(self);

}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {

    _error = error;
    _block(self);

}

Now you can do the follwing, some kind of callback function:

NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://..."];

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setValue:@"application/json" forHTTPHeaderField:@"accept"];

[request fetchDataWithResponseBlock:^(FetchResponse *response) {

    if (response.error || response.statusCode != 200) 
        NSLog(@"Error: %@", response.error); 

    else {

        //use response.data
    }

}];

Here you can find the orginal german solution by ICNH: Asynchrones I/O mit Bloecken

Thank you very much for this!

Tobias Bambullis
  • 736
  • 5
  • 17
  • 45
0

My suggestion would be to use some other delegate methods for NSURLConnection like connection:didReceiveResponse: or connection:didReceiveData:. You should probably keep a use a set up like so:

@interface MyClass : NSObject {
…
    NSMutableData *responseData;   
}
…
@end

- (void)startConnection {
    NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:request delegate:self];
    if (theConnection) {
        responseData = [[NSMutableData data] retain];
    } else {
        // connection failed
    }
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    // connection could have been redirected, reset the data
    [responseData setLength:0];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [responseData appendData:data];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    // connection is done, do what you want
    ………
    // don't leak the connection or the response when you are done with them
    [connection release];
    [responseData release];
}
// for your authentication challenge
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace (NSURLProtectionSpace *)protectionSpace {
    return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}

- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
    if ([trustedHosts containsObject:challenge.protectionSpace.host])
      [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];

    [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
}
Alex Zielenski
  • 3,591
  • 1
  • 26
  • 44
  • thank you for your answer. Yesterday I've found a solution which is much easier - i will post it tomorrow. I've got more than 10 similar views so it is very hard to write this lines again and again ;o) – Tobias Bambullis Nov 30 '11 at 21:59