Reskit - callback on the same thread instead of UI thread - ios

I am performing getObjectsAtPath in a background thread. However, the success/failure blocks are called on the UI thread.
Is there a way to force RestKit to call success and failure blocks on the same thread instead on the UI thread?

Restkit does not provide this functionality, but you can archive this.
RKObjectRequestOperation class has two properties successCallbackQueue & failureCallbackQueue, which are allows you to set call back queue. Overwrite RKObjectManager class and return RKObjectRequestOperation then you can set callback queues.
- (RKObjectRequestOperation *)getObjectsAtPath:(NSString *)path
parameters:(NSDictionary *)parameters
success:(void (^)(RKObjectRequestOperation *operation, RKMappingResult *mappingResult))success
failure:(void (^)(RKObjectRequestOperation *operation, NSError *error))failure
{
NSParameterAssert(path);
RKObjectRequestOperation *operation = [self appropriateObjectRequestOperationWithObject:nil method:RKRequestMethodGET path:path parameters:parameters];
[operation setCompletionBlockWithSuccess:success failure:failure];
[self enqueueObjectRequestOperation:operation];
return operation;
}
Then you can set callback queues as shown bellow:
RKObjectManager *objectManager = [RKObjectManager sharedManager];
RKObjectRequestOperation *operation = [objectManager getObjectsAtPath:path
parameters:parameters
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
}
failure:^(RKObjectRequestOperation *operation, NSError *error) {
}];
operation.successCallbackQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
operation.failureCallbackQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

Related

AFNetworking - stuck after posting http data

I've inherited some Objective-C code from 2013: so old that it used AFNetworking 1.0!
#implementation AFClaimNotificationAPIClient
+ (AFClaimNotificationAPIClient *)sharedClient {
static AFClaimNotificationAPIClient *_sharedClient = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedClient = [[AFClaimNotificationAPIClient alloc] initWithBaseURL:[NSURL URLWithString:kClaimNotificationURL]];
});
return _sharedClient;
}
- (void) submit:(ClaimNotification *) claimNotification
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure {
NSMutableURLRequest *request = [self multipartFormRequestWithMethod:#"POST" path:kClaimNotificationURL parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[self populateFormDataForJson:formData andClaimNotification:claimNotification];
[self populateFormDataWithAttachemnts:formData andClaimNotification:claimNotification];
}];
AFJSONRequestOperation *operation = [[AFJSONRequestOperation alloc] initWithRequest:request];
DDLogVerbose(#"%#", request.HTTPBody);
[operation setCompletionBlockWithSuccess:success failure:failure];
[operation start];
}
In the corresponding header file for this class, the AFClaimNotificationAPIClient is defined thus:
#interface AFClaimNotificationAPIClient : AFHTTPClient
and AFHTTPClient no longer exists. It was dropped in AFNetworking 2.0, which came out shortly after this code was written.
After much forum searching, I've actually managed to get it partially working again by upgrading to AFNetworking 2.0, and redefining AFClaimNotificationAPIClient as an AFHTTPSessionManager:
#interface AFClaimNotificationAPIClient : AFHTTPSessionManager
My submit button code now looks like this:
- (void) submit:(ClaimNotification *) claimNotification
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure {
NSURLSessionDataTask *request = [self POST:kClaimNotificationURL parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[self populateFormDataForJson:formData andClaimNotification:claimNotification];
[self populateFormDataWithAttachemnts:formData andClaimNotification:claimNotification];
} success:^(NSURLSessionDataTask *task, id responseObject) {
DDLogVerbose(#"Post success");
// handle success
} failure:^(NSURLSessionDataTask *task, NSError *error) {
DDLogVerbose(#"Post error");
}];
// [operation start];
[request resume]; // [request start] doesn't work
}
I chose AFHTTPSessionManager as the new type for my class because it's the only one that I could find that contains the constructingBodyWithBlock definition, and I was trying to keep the code as close to the original as possible.
Amazingly enough, my reworked code actually posts data to the server and gets a reply. However, the app hangs at that point because the calling code (not shown here) is not receiving a success (or failure) message. I can see that I've removed a whole step from the original code - the setting up of the operation variable and then the triggering of its setCompletionBlockWithSuccess method.
You need to execute the block according to the response passing a responseObject or an error with the corresponding operation
try this
- (void) submit:(ClaimNotification *) claimNotification
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure {
NSURLSessionDataTask *request = [self POST:kClaimNotificationURL parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[self populateFormDataForJson:formData andClaimNotification:claimNotification];
[self populateFormDataWithAttachemnts:formData andClaimNotification:claimNotification];
} success:^(NSURLSessionDataTask *task, id responseObject) {
DDLogVerbose(#"Post success");
// handle success
success(nil,responseObject);
} failure:^(NSURLSessionDataTask *task, NSError *error) {
DDLogVerbose(#"Post error");
failure(nil, error);
}];
// [operation start];
[request resume]; // [request start] doesn't work
}
You can use AFHTTPRequestOperation as well and this will return both parameters needed in your callbacks

Restkit 2.0 cancellation - any hidden issues using it?

I'm just started with codebase that is using RestKit 2.0 .
One of the problems in the codebase is abundance of network calls that are hard to cancel.
I see that some networking methods could easily return operation to the caller, so that caller could cancel them at will. However, interface doesn't support that easily. Have anyone tried writing their own category to expose return these operations?
For example,
- (void)getObjectsAtPath:(NSString *)path
parameters:(NSDictionary *)parameters
success:(void (^)(RKObjectRequestOperation *operation, RKMappingResult *mappingResult))success
failure:(void (^)(RKObjectRequestOperation *operation, NSError *error))failure
{
NSParameterAssert(path);
RKObjectRequestOperation *operation = [self appropriateObjectRequestOperationWithObject:nil method:RKRequestMethodGET path:path parameters:parameters];
[operation setCompletionBlockWithSuccess:success failure:failure];
[self enqueueObjectRequestOperation:operation];
}
Could look like this:
- (NSOperation *)getObjectsAtPath:(NSString *)path
parameters:(NSDictionary *)parameters
success:(void (^)(RKObjectRequestOperation *operation, RKMappingResult *mappingResult))success
failure:(void (^)(RKObjectRequestOperation *operation, NSError *error))failure
{
NSParameterAssert(path);
RKObjectRequestOperation *operation = [self appropriateObjectRequestOperationWithObject:nil method:RKRequestMethodGET path:path parameters:parameters];
[operation setCompletionBlockWithSuccess:success failure:failure];
[self enqueueObjectRequestOperation:operation];
return operation;
}
Please let me know if anyone tried this and run into issues.
That isn't the only operation involved in the process. It deals with the networking aspect but not the mapping. That doesn't mean it's not possible, but it isn't necessarily as easy as you think.
RKObjectManager provides an interface for cancelling in-flight operations based on the request rather than the specific operation which is more appropriate to use.

AFNetworking : The Operation couldn't completed.(NSURLErrorDomain error -1004)

Some time, my server slow to response and I got this message in response, The Operation couldn't completed.(NSURLErrorDomain error -1004)
How to handle such errors?
P.S. I'm using AFNetworking for this.
You may increase the time-out by overriding the get/post methods
Following is the example for get method
- (AFHTTPRequestOperation *)GET:(NSString *)URLString
parameters:(id)parameters
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:#"GET" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:nil];
[request setTimeoutInterval:ADD_YOUR_TIME_OUT_INTERVAL];
AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithRequest:request success:success failure:failure];
[self.operationQueue addOperation:operation];
return operation;
}
P.S. I used this approach in the previous version of AFNetwork, by subclassing AFHTTPClient. I didn't test it in the new version's AFHTTPRequestOperationManager.
This may be helpful : https://stackoverflow.com/a/22666837/1292441

RKObjectManager sharedManager getObjectsAtPath Optionally save in persistent storage

[[RKObjectManager sharedManager] getObjectsAtPath:#"/pathpattern/:userId" parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSLog(#"success");
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"fail");
}];
I am using RKManagedObjectStore using addSQLitePersistentStoreAtPath:fromSeedDatabaseAtPath:withConfiguration:options:error
The defined pattern gives different data for different "userId", I want to save data in sqlite data base for only one userId, for other userIds I don't want to save it.
Any suggestion how this could be achieved using same [RKObjectManager sharedManager]
You can use an RKDynamicMapping with the response descriptor for that path. With that mapping you can analyse the incoming data and decide which mapping you actually want to apply to the received data.
Note that 'not saving' the new objects to Core Data means using an RKObjectMapping instead of RKEntityMapping.
you don`t init your shared manager. try this.
NSURL *url = [NSURL URLWithString:yourURL];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
RKObjectManager *operation = [[RKObjectManager alloc] initWithHTTPClient:httpClient];
[[RKObjectManager sharedManager] getObjectsAtPath:kURLToDownloadData
parameters:nil
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult){
NSLog(#"success");
}
failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"fail");
}

AFNetworking miss implemetation

AFHTTPRequestOperationManager has this implementation:
- (AFHTTPRequestOperation *)HTTPRequestOperationWithRequest:(NSURLRequest *)request
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.responseSerializer = self.responseSerializer;
operation.shouldUseCredentialStorage = self.shouldUseCredentialStorage;
operation.credential = self.credential;
operation.securityPolicy = self.securityPolicy;
[operation setCompletionBlockWithSuccess:success failure:failure];
return operation;
}
when use this method, the success and failure blocks never get call. After I put this line in the implementation:
[self.operationQueue addOperation:operation];
it works. Why AFNetworking 2.0 AFHTTPRequestOperationManager miss this line or I just don't understand this method? Thanks.
HTTPRequestOperationWithRequest creates an operation, but does not execute it. When you add the operation to the operation queue you created with this call:
[self.operationQueue addOperation:operation];
you are essentially executing the operation you just created. Then the success and failure blocks will get called.

Resources