views:

26

answers:

1

I have a REST service that uses an auth_token that expires every so often. when a request fails, I want to re-authenticate (which I'm able to do) and then resend the same exact TTURLRequest in the following generic way:

- (void)request:(TTURLRequest*)request didFailLoadWithError:
(NSError*)error {
       NSLog(@"error %@ %@ %@", [error localizedDescription], [error
localizedFailureReason], [error localizedRecoverySuggestion]
                 );


       if (numRetries == 0) {
               [self authenticateUser:nil];

               request.urlPath = [request.urlPath
stringByReplacingOccurrencesOfRegex:@"access_token=([\\w-]+)"
withString:[NSString stringWithFormat:@"access_token=%@",
accessToken]];

               NSLog(@"URL: %@", request.urlPath);
               [request   send];

               numRetries++;

       }

}

all of my TTURLRequests use the same delegate which uses this failure method. but for some reason, when I call [request send] the request gets to the "loading" phase, but does not ever seem to complete. However, if i do a manual refresh (by dragging down the table view) it re-generates the TTURLRequest from scratch and seems to work fine. I really just need to know what the correct way to 'resend' this request is...

A: 

I recently came across the same issue. I haven't explored Three20 deeply enough to know if retrying failed requests is intended, but I found the problem and a reliable (and simple) fix.

After [request send] is called the second time, the TTURLRequest instance gets deallocated before it finishes loading. If you look at TTURLRequestModel.m in Three20Network, you can see that the model's last request (_loadingRequest) gets released before the new request gets retained. If the last request happens to be the same as the new request (as it is in your case) it's retain count goes to 0 and it gets dealloc'd:

/////////////////////////////////////////////////////////////////////////////
- (void)requestDidStartLoad:(TTURLRequest*)request {
  [_loadingRequest release];
  _loadingRequest = [request retain];
  [self didStartLoad];
}

I solved this by subclassing TTURLRequestModel.m and overriding this method with the following:

/////////////////////////////////////////////////////////////////////////////////
- (void)requestDidStartLoad:(TTURLRequest*)request {
  [request retain];
  [_loadingRequest release];
  _loadingRequest = request;
  [self didStartLoad];
}

This worked in my testing and doesn't seem like it should affect anything negatively.

Note: If you are using TTURLJSONResponse for your request's response object, you will need allocate a new instance and set it for request.response before calling send again. If you look at TTURLJSONResponse.m in extThree20JSON, you'll see that the processResponse method asserts (nil == _rootObject). This will fail if the response instance has been used in a request previously.

jm333