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.
Related
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
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.
I am using AFNetworking to access a URL with Windows Authentication. I was using ASIHTTPRequest to login like so:
-(BOOL)User:(NSString *)user andPassWordExists:(NSString *)password
{
NSURL *url = [NSURL URLWithString:kIP];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setUseSessionPersistence:YES];
[request setUseKeychainPersistence:NO];
[request setUsername:user];
[request setPassword:password];
[request setDomain:#"domain"];
[request startSynchronous];
NSError *loginError = [request error];
if(loginError == nil){
return true;
}else{
return false;
}
}
and now I am trying to do the same thing with AFNetworking and this what I came up with from this example: http://www.raywenderlich.com/forums/viewtopic.php?f=2&t=18385
-(BOOL)User:(NSString *)user andPassWordExists:(NSString *)password
{
NSURL *url = [NSURL URLWithString:kIP];
NSURLRequest *request = [NSURLRequest requestWithURL:url
cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData
timeoutInterval:90.0];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc]
initWithRequest:request];
[operation setCredential:[NSURLCredential credentialWithUser:[#"domain" stringByAppendingString:user]
password:password persistence:NSURLCredentialPersistenceNone]];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
return true;
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
return false;
}];
[operation start];
}
but this gives me two errors:
/Users/jsuske/Documents/SSiPad(Device Only)ios7/SchedulingiPadApplication/Classes/LHJSonData.m:148:46: Incompatible block pointer types sending 'int (^)(AFHTTPRequestOperation *__strong, __strong id)' to parameter of type 'void (^)(AFHTTPRequestOperation *__strong, __strong id)'
/Users/jsuske/Documents/SSiPad(Device Only)ios7/SchedulingiPadApplication/Classes/LHJSonData.m:152:15: Incompatible block pointer types sending 'int (^)(AFHTTPRequestOperation *__strong, NSError *__strong)' to parameter of type 'void (^)(AFHTTPRequestOperation *__strong, NSError *__strong)'
How Can I create a method that will use user and password to login and store those credentials...this is possible with AFNetworking, it sure was with ASIHTTPRequest
I think you are misunderstanding blocks. You are returning TRUE/FALSE from the callback blocks, not your method. Those blocks will be executed at some point in the future, and their return type is void. Try running this code to see the order:
-(void)User:(NSString *)user andPassWordExists:(NSString *)password
{
// construct your request
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"In success block");
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"In Fail block");
}];
[operation start];
NSLog(#"Network op started");
}
As to your design, I would not return true or false, you have to find another way to manage the success and fail scenarios, which could look something like this (I don't know enough about your overall design to give a definitive answer):
-(void)User:(NSString *)user andPassWordExists:(NSString *)password
{
// construct your request
__weak typeof(self) weakSelf = self;
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
// called at somepoint in the future if the request is success full
[weakSelf handleSuccess:responseObject];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// called at somepoint in the future if the request fails
[weakSelf handleFail:error];
}];
[operation start];
}
- (void)handleSuccess(id)response {
// process the response
}
- (void) handleFail:(NSError*)error {
// evaluate error
}
You see the bock declaration here: https://github.com/AFNetworking/AFNetworking/blob/master/AFNetworking/AFHTTPRequestOperation.h
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
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);