How to automatically handle token refreshing with RestKit - ios

I have a project which you can imagine is like the Facebook app and I'm having trouble grasping how to organize it with RestKit and handle tokens since I'm a RestKit beginner.
This is what I got so far:
Core Data NSManagedObject classes, for each of them I added a category called REST that has functions to provide RKObjectMapping and RKRouteSet
custom RKObjectManager subclass which handles setting up headers, MIME types, setting authorization headers and Core Data stores
RKObjectRequestOperation subclass
If you have organizational advice shoot, but my main question is how do I implement automatic token refreshing after recieving a 401 HTTP status code and firing the failed request again with a fresh token?
I started by overriding the metod in my RKObjectRequestOperation subclass:
- (void)setCompletionBlockWithSuccess:(void ( ^ ) ( RKObjectRequestOperation *operation , RKMappingResult *mappingResult ))success
failure:(void ( ^ ) ( RKObjectRequestOperation *operation , NSError *error ))failure
So now I can intercept a 401 response, but now I have to make another call to refresh the token and then re-enqueue the failed operation with a fresh token, please advise how to do that.

I generally wouldn't use a custom RKObjectRequestOperation if you are using an object manager. I'd handle it at the object manager level.
In the object manager you can override each of the main request methods (which are supplied with success and failure blocks). You can implement these methods to call super but substituting the success and failure blocks with new versions which add some logic before calling the supplied versions.
Now, the additional logic would be a check of the response status. If it shows a token refresh is required it can be actioned and then the object manager can trigger a new load, using the parameters supplied in the first request.
Also, you shouldn't re-queue an operation unless the documentation states that it is supported as some operations won't work the second time they're executed...

Related

Add new AFHTTPRequestOperation with dependency on existing request

Sending few post request using
- (AFHTTPRequestOperation *)POST:(NSString *)URLString
parameters:(id)parameters
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
I want to perform one get request once all of the above request have processed so I have created AFHTTPRequestOperation and adding dependency as
for (AFHTTPRequestOperation *operation in manager.operationQueue.operations ) {
[AFHTTPRequestOperationObject addDependency:operation];
}
[manager.operationQueue addOperation: AFHTTPRequestOperationObject];
But the operation is performed before the completion of existing post request.
You shouldn't use NSOperation dependencies to solve this problem. In your case, later operations rely on processing with completionBlock but NSOperationQueue and AFNetworking both consider that work a side effect.
completionBlock is:
the block to execute after the operation’s main task is completed.
(my emphasis).
In the case of AFHTTPRequestOperation, "the operation’s main task" is "making an HTTP request". The "main task" doesn't include parsing JSON, persisting data, checking HTTP status codes, etc. - that's all handled in completionBlock.
To perform one request after other requests have processed, you'll need to make that request in the completion handler, once you've verified you have all the data you need to proceed.
While using OperationQueues, it is not necessary that the responses will be received in the same order as the requests made. You will have to make sure that you make the GETrequest after complete execution of multiple POST requests. This explains the process to perform the operations serially with a completionHandler (which in your case should be the GET request that you want to execute at the completion of the POST requests.)
It appears that AFHTTPRequestOperation(s) complete as soon as the HTTP call is made and returns. The success and failure blocks are called after these operations are complete. So if you have NSOperation A dependent on B which is a AFHTTPRequestOperation then you may see B run, followed by A, followed by B's success block. Seems to work if you create a "finished" operation and make A dependent on it. Then have B enqueue the "finished" operation for running when B calls its success or failure block. The "finished" operation in effect signals A that B has done all you wanted it to do.

AFNetworking 2.0 - Forced caching

Is it possible to force response caching if it contains neither Expires or Cache-Control: max-age?
I've came across this article, but unfortunately URLSession:dataTask:willCacheResponse:completionHandler: never gets called in my AFHTTPSessionManager subclass.
Any help appreciated.
You can force the caching by implementing your own NSURLProtocol that does not follow the standard HTTP caching rules. A complete tutorial is here, which persists the data using Core Data, but the basic steps are:
Subclass NSURLProtocol
Register your subclass with +registerClass:
Return YES in your +canInitWithRequest: method if this is the first time you've seen request, or NO if it isn't
You now have two choices:
Implement your own cache storage (in which case, follow the tutorial linked above)
Inject the cache control headers that you wish the URL loading system to follow
Assuming you want #2, override connection:didReceiveResponse: in your protocol subclass to create a response that has the cache control headers you want to emulate:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
// Create a dictionary with the headers you want
NSMutableDictionary *newHeaders = [response.allHeaderFields mutableCopy];
newHeaders[#"Cache-Control"] = #"no-transform,public,max-age=300,s-maxage=900";
// Create a new response
NSHTTPURLResponse *newResponse = [[NSHTTPURLResponse alloc] initWithURL:response.URL
statusCode:response.statusCode
HTTPVersion:#"HTTP/1.1"
headerFields:newHeaders];
[self.client URLProtocol:self
didReceiveResponse:newResponse
cacheStoragePolicy:NSURLCacheStorageAllowed];
}
This will cause the response to be cached as if the server had provided these headers.
For URL sessions only, you need to set the session configuration's protocolClasses. Since you're using AFNetworking, that looks like:
[AFHTTPSessionManager sharedManager].session.configuration.protocolClasses = #[[MyURLProtocol class]]
There are some caveats, so make sure you read the protocolClasses documentation.
A few notes:
If there's any way to fix this by having your server send the appropriate headers, please, please do that instead.
For the sake of brevity I hardcoded "HTTP/1.1", but technically you should pull this out of the response.
AFNetworking uses the standard URL Loading System, and is mostly unrelated to this issue.

Best way to chain multiple NSUrlConnection in iOS

I couldn't find a reasonable answer so i'll ask for this specific case.
I must perform a http call (setting a specific header as a token value) and parse the returned json. The json can both return a "operation completed" message or a "token expired" message.
In case of token expired i must execute another http call which will provide me the refreshed token, set the token as header and re-execute the original http call.
I decided to adopt this solution: from the main thread, i'm gonna create another thread using
...
dispatch_async(feedQueue
...
and in this thread i'm gonna performing the above descripted calls as synchronous calls using
...
[NSURLConnection sendSynchronousRequest:urlRequest
...
and only at the end of the flow i call the main thread passing results to a block.
This way code is simple, easy to read, it has a linear flow, it is completely scoped inside a unique thread, and i don't mess with delegates and different "finite-states" to manage calls chain.
But i'm not sure if this is the best approach for my specific use-case as i've read similar questions where people suggest to adopt asynchronous calls, using both finite-states or NSOperation instances, but it seems to me that both these approaches based on asynchronous calls, are not the best solutions for a http calls chain.
What is the best approach? How can i correctly implement a chain of http call?
The correct way to set this up would be to use asynchronous calls for NSURLConnection. In the connectionDidFinishLoading you will intercept the connection that just finished and launch the next chained http connection.
Setup a private variable called _connection1;
So basically, you will do something like this
NSURLRequest * request1 = ...
_connection1 = [[NSURLConnection alloc] initWithRequest:request1 delegate:self];
In the
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
if([connection isEqual:_connection1]){
NSURLRequest *request2 = ...
_connection2 = [NSURLConnection...]
}
if([connection isEqual:_connection2]){
NSURLRequest *request3 = ...
_connection2 = [NSURLConnection...request3 ]
// And so on ....
}
}

RestKit: How to resubmit failed request after re-authenticating?

The API I'm developing against requires me to present an authentication token in a custom HTTP header. This token expires every few minutes, and could happen while the user is still within the app as long as they have been idle long enough. When the token has expired I receive a 403 response but I only find out after attempting a request.
What's the best way to get RestKit to automatically reauthenticate and retry the request so I don't have to put in this logic everywhere I make a request? Responses to similar questions have suggested using the RKRequestDelegate protocol or the RKObjectLoaderDelegate protocol but unfortunately these are no longer part of RestKit (as of 0.20).
Any idea what the "correct" approach should be now? Should I be subclassing RKObjectManager and tacking on a retry to each of the request operations or should I provide a custom HTTPOperation or HTTPClient class or is there some better approach altogether? Thanks!
Catch it in Failure block , and check for the status code and re-do the authentication
RKObjectRequestOperation *requestOp = [[RKObjectRequestOperation alloc] initWithRequest:request responseDescriptors:#[getObjResp]];
[requestOp setCompletionBlockWithSuccess:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
....
}
} failure:^(RKObjectRequestOperation *operation, NSError *error){
// Here your status code check
// Here your retry-code
}

With iOS asynchronous http request, how do you connect the received data to the request?

I'm loading certain images from a certain server asynchronously. I'm firing a number of requests at once using NSURLConnection connectionWithRequest method and receive the data using NSURLConnectionDelegate didReceiveData.
At didReceiveData, how do I know which request this data matches? At didReceiveResponse I can use the URL method of the response given as a parameter, but in didReceiveData I only have the received data.
It seemed like the perfect answer would be to use NSURLConnection sendAsynchronousRequest, as the completion handler has all the required parameters: (NSURLResponse*, NSData*, NSError*). I can use [response URL] to make a match to the original request... except in one case: not all the images I try to download exist. In that case, the request is redirected to a generic error page and the URL of that generic page is received, so I can't match the response to a request I've made. This I could handle with connectionWithRequest, but I can't get the best of both worlds.
In
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
you can use
NSURLRequest *request = [connection originalRequest];
to get the request that the connection was started with.
(This method is available since iOS 5.0, but I could not find it in my Xcode iOS 5.1 Library. You find it in the iOS 6 Library, in the header file NSURLConnection.h or here: http://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/NSURLConnection_Class/Reference/Reference.html#//apple_ref/occ/instm/NSURLConnection/originalRequest).
More methods to manage multiple connections can be found in this thread: Managing multiple asynchronous NSURLConnection connections.
If you use sendAsynchronousRequest:queue:completionHandler: then you can just use the request parameter in the completion block.
At connection:didReceiveData: the first parameter is the NSURLConnection instance. So I don't understand where the problem is. You create a connection, then you send a request to that connection and the delegate receive the connection:didReceiveData with the connection value.
If you are using the same delegate for all the request you have to check the connection so you can say which request is associated to.
Perhaps you have to maintain a table of connection/request pairs.

Resources