Julian J. Tejera
Julian J. Tejera

Reputation: 1021

NSURLConnection Data Retrieval

I'm having a very puzzling issue with NSURLConnection. It is returning the data in another thread or something, things are not working in the proper order. I'm supposed to receive the json data then parse it, but the data is actually being received after the parser fails. Here is the log.

2013-09-22 14:44:40.454 WebServiceExample[39306:a0b] Parsing Error: Error Domain=NSCocoaErrorDomain Code=3840 "The operation couldn’t be completed. (Cocoa error 3840.)" (No value.) UserInfo=0xa0b47e0 {NSDebugDescription=No value.} 2013-09-22 14:44:41.312 WebServiceExample[39306:a0b] Succeeded! Received 112 bytes of data

I'm making a small framework to ease my life to connect to rails API, I'm calling it objective rails OR, but I already spent the entire weekend trying to figure out what I'm doing wrong. Here's the code:

@interface ORObject : NSObject

@property (nonatomic, retain) NSMutableDictionary *properties;
@property (nonatomic, retain) ORWebManager *webManager;
@property (nonatomic, retain) NSString *route;
@property (nonatomic, retain) NSString *singularClassName;

- (id) initWithRoute:(NSString *) route webManager:(ORWebManager *) webManager;
- (id) initWithRoute:(NSString *) route;
- (ORObject *) find:(NSInteger) identifier;
@end

Here is the implementation of find:

- (ORObject *) find:(NSInteger) identifier
{

    NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@/%@/%ld.%@", [    ORConfig sharedInstance].baseURL, self.route, (long)identifier, self.webManager.    parser.contentType]];

    ORObject *object = [self.webManager httpGet:url];

    if (object) {
        object.route = self.route;
        object.webManager = self.webManager;
    }

    return object;
}

Here is the httpGet method of the ORWebManager class

- (ORObject *) httpGet:(NSURL *)url
{
    ORGetRequester *requester = [[ORGetRequester alloc] init];
    NSMutableData *data = [requester request:url];
    return [self.parser toObject:data];
}

The superclass of ORGetRequester is ORHTTPRequester and it implements the NSURLConnectionDelegate protocol methods

@interface ORHTTPRequester : NSObject<NSURLConnectionDelegate>
@property (nonatomic, retain) NSMutableData *receivedData;
@property (nonatomic, retain) NSMutableURLRequest *req;
@property (nonatomic, retain) NSURLConnection *connection;
@end

@implementation ORHTTPRequester
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse
    [self.receivedData setLength:0];
}

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

- (void)connection:(NSURLConnection *)connection
  didFailWithError:(NSError *)error
{
    NSLog(@"Connection failed! Error - %@ %@",
          [error localizedDescription],
          [[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    NSLog(@"Succeeded! Received %d bytes of data",[self.receivedData length]);
}   
@end

And finally here is ORGetRequester, this is where the "magic" happens.

@interface ORGetRequester : ORHTTPRequester
- (NSMutableData *) request:(NSURL *)url;
@end

@implementation ORGetRequester
- (NSMutableData *) request:(NSURL *)url
{
    self.req = [NSMutableURLRequest requestWithURL:url
                                              cachePolicy:NSURLRequestUseProtocolCachePol    icy
                                          timeoutInterval:60.0];
    self.receivedData = [[NSMutableData alloc] init];
    self.connection = [[NSURLConnection alloc] initWithRequest:self.req delegate:self];

    if (!self.connection) {
        NSLog(@"Connection Failed");
    }

    return self.receivedData;
}
@end

Upvotes: 0

Views: 128

Answers (1)

zaph
zaph

Reputation: 112873

self.connection = [[NSURLConnection alloc] initWithRequest:self.req delegate:self];

returns immediately because it starts an async operation. You need to parse the data when connectionDidFinishLoading: is called.

Or consider using:

+ (void)sendAsynchronousRequest:(NSURLRequest *)request queue:(NSOperationQueue *)queue completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))handler

The completion block will be called when the http operations is complete. Process the data in the completion block and notify any object that needs the data. This is probably a good bet if you do not need the other delegate methods.

Upvotes: 1

Related Questions