So I am trying to access a json file online and I can get the contents of the JSON file when I run the completion block using AFHTTPRequestOperation method. But when I try to NSLog outside the completion block, it becomes null. I was wondering why this is happening and how I would go about fixing this. Below is the code I have so far:
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCredential:credentials];
[operation setResponseSerializer:[AFJSONResponseSerializer alloc]];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Success %#", responseObject);
jsonOutputData = [NSMutableArray arrayWithObject:responseObject];
self.newsFeedArray = [NSMutableArray arrayWithArray:[jsonOutputData copy]];
}failure:^(AFHTTPRequestOperation *operation, NSError *error){
NSLog(#"Failure: %#", error);
}];
[manager.operationQueue addOperation:operation];
NSLog(#"%#", self.newsFeedArray);
The NSLog with Success shows the JSON but the NSLog with the self.newsFeedArray shows (null). I am just wondering why this is happening. If you need more clarification, let me know. Any help would be appreciated!
Thanks!
The success and failure blocks get called asynchronously. So self.newsFeedArray simply has not been set yet at the time of the last NSLog call, because the operation has not completed.
You could wait for the operation to complete (e.g., [operation waitUntilFinished]) but you almost certainly don't want to do this, especially on the main thread. Rather have the completion blocks trigger the desired behaviour.
Related
This question already has an answer here:
Fetching JSON data after failed retrieval
(1 answer)
Closed 7 years ago.
I'm trying to get this UIAlertView to run when fetching the JSON data fails, but i can't seem to get it to work. I'd appreciate it if someone could show me how to do it or point me in the right direction!
I think you have forget to write failure block.
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc]initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"success: %#", operation.responseString);
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"error: %#", operation.responseString);
}];
Hope this will help you.
You can write this in your block
NSDictionary *headersCollection=[[(NSDictionary *)operation valueForKey:#"response"]valueForKey:#"allHeaderFields"];
NSMutableDictionary *headers=[headersCollection mutableCopy];
headers[#"statusCode"]=[NSNumber numberWithInteger:operation.response.statusCode];
and then check the status code value. Headers dictionary will provide you the complete header which is returned. If statuscode is 200 everything is OK else you can show your custom message accordingly with different status code values like 400, 415 etc
I am using AFnetworking for authentication, but after Authenticating user I need to move to the next View controller. It's moving, but also it moves when there's an error too. How can I make use of the responseObject in AFNetworking to my need...... Below is my CODE
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
//[operation setCredential:credential];
[operation setResponseSerializer:[AFJSONResponseSerializer alloc]];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
//This is where if the response is successful it should move
if ([responseObject objectForKey:#"Success"]) {
MainView *home = [self.storyboard instantiateViewControllerWithIdentifier:#"MainViewController"];
[self.navigationController pushViewController:home animated:YES];
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failure: %#", error);
}];
[manager.operationQueue addOperation:operation];
To check if success or not, you are using :
if ([responseObject objectForKey:#"Success"])
I don't know your service and the retrieve Diccionary, however I think you always have an object for the key #"Success", and because #"Success" exists is passing in all the scenarios.
Try to check the value, e.g.:
if ([[responseObject objectForKey:#"Success"] isEqualsToString #"ok"])
I download asynchronously some object, I store it in array. Next for each object I download some coordinates with geocoding (it is also asynchronously), and update my database for each object with new parameters which is coordinate. My method looks like this:
- (void)downloadObjectsWithTitle:(NSString *)title andHandler:(void(^)(NSMutableDictionary *result))handler {
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
NSMutableURLRequest *request = [httpClient requestWithMethod:#"GET"
path:nil
parameters:nil];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[httpClient registerHTTPOperationClass:[AFHTTPRequestOperation class]];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
//I get here array of objects
//now for each object I want to download geocoding localization so i called another asynchronyous method getLocationWithTitle:andHandler;
for(int i = 0; i < resutArray.count; i++) {
[self downloadLocationWithString:[dictionary objectForKey:#"string"] andHandler:^(NSMutableDictionary *result) {
//update database;
}];
}
handler(dictionary);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
[operation start];
}
My question is how to downalod coordinates for each object and that fire:
handler(dictionary);
so wait for each coordinates download (for each object) before quit method (fire handler).
Thnaks for all sugestions.
Maintain a count of all the tasks. When it's zero you're done.
Assuming you're using dispatch_async in downloadLocationWithString: on a concurrent queue:
dispatch_barrier_async(queue, ^{
// will only be called after all the blocks submitted to queue have finished.
}];
(If you're using serial queue, simply call handler at the last line of the last block)
Try a global flag. set NO first. In download block, after download complete set flag to yes. You can check that flag.
I am using Restkit in my iOS app to make a GET call to my server. I am able to get the call to work just fine, except that it is supposed to be asynchronous and it is blocking my main thread. I am basically using their exact sample to make the request from their github page which is as follows:
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://mywebapi.com/Article"]];
RKObjectRequestOperation *operation = [[RKObjectRequestOperation alloc] initWithRequest:request responseDescriptors:#[responseDescriptor]];
[operation setCompletionBlockWithSuccess:^(RKObjectRequestOperation *operation, RKMappingResult *result) {
Article *article = [result firstObject];
NSLog(#"Mapped the article: %#", article);
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"Failed with error: %#", [error localizedDescription]);
}];
[operation start];
This blocks all main thread executions (such as my transitions) until the success block is called. Wrapping this call inside of a dispatch_queue does resolve the issue, but it is my understanding that this method is supposed to be asynchronous on its own.
Am I missing some configuration on the RKObjectRequestOperation, or is there a different method I should be calling for an async call?
I found the answer to this question here:
RestKit Makes UI Unresponsive
Basically, you do need to add it to an NSOperationQueue, otherwise it will run the call on the main thread.
I want the function do not return until the AFHTTPRequestOperation finished, but I did not know how to do it, thanks in advance.
-(BOOL)download
{
BOOL ret = TRUE;
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
ret = [self handle:data];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failure: %#", error);
}];
[operation start];
return ret ;
}
Your design is incorrect.
AFHTTPRequestOperation is asynchronous so you cannot (and you shouldn't) treat it in a synchronous way. You have to modify your workflow in order to use the completion or failure blocks of the AFHTTPRequestOperation.
Since AFNetworking is asynchronous this isn't possible. When using async requests you should always call your finishing code within the success/finish block.
If you explain where you are using the download method and why you need to know when it's finished I can help explain/ help you design it better.
I would agree with the others above that generally you should stick with AFNetworking Asynchronous nature, but there are ways to cause pseudo synchronous code to run for AFNetworking requests.
Using your example the code below should work.
-(BOOL)download {
BOOL ret = TRUE;
__block BOOL complete = NO;
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
ret = [self handle:data];
complete = YES;
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failure: %#", error);
complete = YES;
}];
[operation start];
while(complete == NO) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
}
return ret;
}
I have found this kind of usage to be particularly useful with unit testing API's. Nesting can become quite annoying if you have to do API calls just to get to the call you want to test. This is a nifty tool to get around that.