AFJSONRequestOperation array populates but cannot NSLog contents outside of success block - iphone

The following code is taken from this tutorial
I've used this snippet before but I never noticed this issue before. NSLog of the array contents prints in a delegate method but not in the viewDidLoad outside of the success block. I require a way to save the JSON data into an array for use elsewhere in the code. I should also add that I'm not using UITableView to display my data. What am I missing or how can I accomplish this?
This does not print the JSON content thought it does populate the array:
#import "AFNetworking.h"
...
- (void)viewDidLoad {
...
self.movies = [[NSArray alloc] init];
NSURL *url = [[NSURL alloc] initWithString:#"http://itunes.apple.com/search?term=harry&country=us&entity=movie"];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
self.movies = [JSON objectForKey:#"results"];
[self.activityIndicatorView stopAnimating];
[self.tableView setHidden:NO];
[self.tableView reloadData];
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
NSLog(#"Request Failed with Error: %#, %#", error, error.userInfo);
}];
[operation start];
NSLog(#"self.movies %#",self.movies); // does not print
...
}
This does print the JSON content: I've only used numberOfRowsInSection as a separate location for the NSLog statement.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (self.movies && self.movies.count) {
NSLog(#"self.movies %#",self.movies); // prints
...
}

You are kicking off an asynch operation and then immediately trying to print out the contents. Move your first NSLog statement into the success block.
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
//the following lines of code execute after the response arrives
self.movies = [JSON objectForKey:#"results"];
NSLog(#"self.movies %#",self.movies);
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
NSLog(#"Request Failed with Error: %#, %#", error, error.userInfo);
}];
[operation start];
//this line of code executes directly after the request is made,
//and the response hasn't arrived yet
NSLog(#"I probably don't have the response yet");

Related

Accessing POST wcf services from iPhone

I have been given url of an asp.net wcf service page which accepts few parameters as POST. But I am getting error code 400 when I try to access it.
url:
http://domain.w04.wh-2.com/IBServiceHost/IBService.svc/AddTask
I am also told to post json as below
{
"TaskId":"23",
"TaskDate":"6/27/2013",
"TaskDesc":"Test task",
"TaskName":"TestName",
"TaskStatus":true,
"ProjectId":"24",
"DbState":"Added"
}
My code to access the service:
AFHTTPClient *client = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:#"http://mydomain.w04.wh-2.com"]];
client.parameterEncoding = AFJSONParameterEncoding;
NSMutableURLRequest *request =
[client requestWithMethod:#"POST" path:#"/IBServiceHost/IBService.svc/AddTask" parameters:dictTask];
AFJSONRequestOperation *operation =
[AFJSONRequestOperation JSONRequestOperationWithRequest:request
success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON)
{
NSLog(#"JSON: %#", [JSON valueForKeyPath:#"LoginResult"]);
NSLog(#"Code: %#", [[[JSON valueForKeyPath:#"LoginResult"] valueForKeyPath:#"Code"] stringValue]);
NSLog(#"FaultString: %#", [[JSON valueForKeyPath:#"LoginResult"] valueForKeyPath:#"FaultString"]);
}
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON)
{
NSLog(#"request %#",request);
NSLog(#"response %#",response);
NSLog(#"error %#",error);
NSLog(#"error opening connection");
}];
[operation start];

Waiting for AFJSONRequestOperation to complete

I'm working with AFNetworking to get some JSON from the web. How can I get the response from the asynchronous request returned? Here's my code:
- (id) descargarEncuestasParaCliente:(NSString *)id_client{
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:#"http://whatever.com/api/&id_cliente=%#", id_client]]];
__block id RESPONSE;
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
RESPONSE = JSON;
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
NSLog(#"ERROR: %#", error);
}];
[operation start];
return RESPONSE;
}
I think you are confused about how blocks work.
That's an asynchronous request, therefore you cannot return any value computed inside the completion block, since your method already returned when it's executed.
You have to change your design an either perform a callback from inside the success block, or pass your own block and get it called.
As an example
- (void)descargarEncuestasParaCliente:(NSString *)id_client success:(void (^)(id JSON))success failure:(void (^)(NSError *error))failure {
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:#"http://whatever.com/api/&id_cliente=%#", id_client]]];
__block id RESPONSE;
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
if (success) {
success(JSON);
}
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
NSLog(#"ERROR: %#", error);
if (failure) {
failure(error);
}
}];
[operation start];
}
You will then call this method like follows
[self descargarEncuestasParaCliente:clientId success:^(id JSON) {
// Use JSON
} failure:^(NSError *error) {
// Handle error
}];

iOS - Performing multiple AFJSONRequestOperations that write to Core Data in background

I am having trouble determining the best way to manage updates to my apps SQLite database (using Core Data)
When my app launches, it hits a server to determine which tables need updating. I then do a service call for each of those tables. Once I get the JSON back for each, it creates/updates the corresponding objects in my SQLite DB.
What I am doing works, as it performs each request and updates each table that needs to be-- but I don't think I am doing this correctly.
Doing this still locks my UI Thread and I need to be able to run this code asynchronously in the background every 10 minutes or so.
AFJSONRequestOperation* operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
NSMutableArray *operations = [NSMutableArray new];
//for each of the tables that need updating, create a AFJSONRequestOperation
for(NSString *str in [JSON valueForKey:#"Views"])
{
NSString* path = [NSString stringWithFormat:#"cache/%#/?deviceUID=%#&token=%#", str, #"00000-00000-0000-00001", [_globals getToken]];
NSURLRequest* request = [client requestWithMethod:#"GET" path:path parameters:nil];
AFJSONRequestOperation* operation2 = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON)
{
[self updateTable:str withJSON:JSON];
}
failure:nil];
[operations addObject:operation2];
}
//AFHTTPClient
[client enqueueBatchOfHTTPRequestOperations:operations progressBlock:nil completionBlock:^(NSArray *operations) {
//this gets called way before the objects are done updating to the DB
NSLog(#"DONE ALL REQUESTS");
[_HUD hide:YES]; // getting called after getting all of the JSON not after all tables are updated
}];
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
[_HUD hide:YES];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Failed" message:[error localizedDescription] delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles: nil];
[alert show];
}];
[operation start];
Heres my updateTable function with only 1 condition
- (void)updateTable:(NSString *)tblName withJSON:(id)JSON
{
for(NSDictionary *record in [JSON valueForKey:#"Records"])
{
NSString *viewName = [[record valueForKey:#"ViewName"] lowercaseString];
//Worker
if([viewName isEqualToString:[[NSString stringWithFormat:#"Worker_vw_iSales"] lowercaseString]])
{
if([Worker doesWorkerExist:[record valueForKey:#"JSONData"]])
{
NSLog(#"deleting old worker");
[ad.managedObjectContext deleteObject:[Worker doesWorkerExist:[record valueForKey:#"JSONData"]]];
}
NSEntityDescription *desc = [NSEntityDescription entityForName:NSStringFromClass([Worker class]) inManagedObjectContext:ad.managedObjectContext];
Worker *worker = [[Worker alloc] initWithEntity:desc insertIntoManagedObjectContext:ad.managedObjectContext];
[worker initWithJSONSting:[record valueForKey:#"JSONData"]];
NSLog(#"Creating Worker: %#", worker.firstName);
}
}
}
I hope this is not all too confusing-- if so I can try to explain more.
I may be doing this completely wrong, and if I am just let me know. I have tried a few other things, including using an NSOperationQueue instead of AFHHTTPs enqueueBatchOfHTTPRequestOperations:requests but I cannot get the behavior I am looking for.
Thanks!
What you are looking for now is setSuccessCallbackQueue: on AFJSONRequestOperation. AFNetworking set all their success blocks to run on the main queue unless otherwise specified.
What I've done is
#implementation myClass {
dispatch_queue_t backgroundQueue;
}
- (id)init
{
if (self = [super init]){
backgroundQueue = dispatch_queue_create("com.proj.myClass", 0);
}
return self;
}
- (void)doSomeStuff
{
NSMutableURLRequest *request = [[myAFAPIClient sharedClient] requestWithMethod:#"GET" path:path parameters:params];
AFJSONRequestOperation *myOperation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
//Success Block
}
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
}];
[myOperation setSuccessCallbackQueue:backgroundQueue];
[[myAFAPIClient sharedClient].operationQueue addOperation:myOperation];
}
So the different would be you are enqueue'ing operations, where I'm just adding them straight to the client.
Also, where I have //Success Block I do all sorts of things such as dispatching other methods onto backgroundQueue, which those methods make more JSON requests and I don't have any issues.

AFNetworking not returning data synchronously inside a block

Can't receive JSON synchronously inside a block using AFNetworking. I checked this solution. It
always nil at the end of method.
Here is my method:
- (BOOL)whois:(NSString *)domain withZone: (NSString*) zone
{
__block NSString *resultCode;
NSURL *url = [[NSURL alloc] initWithString:#"myurl"];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
resultCode = [JSON valueForKeyPath:[NSString stringWithFormat:#"%#.%#", domain,zone]]; //checked with NSLog, works well
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
NSLog(#"Request Failed with Error: %#, %#", error, error.userInfo);
}];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation: operation];
[operation waitUntilFinished];
if(resultCode == #"available") //nil here
{
return YES;
}
return NO;
}
Instead of creating aNSOperationQueue, start your AFJSONRequestOperation with [operation start] and then call [operation waitUntilFinished] and it will block the main thread until it's finished. Then your resultCode should not be nil.
As #mattt said in the post you linked, it is strongly discouraged to freeze the thread like this. Consider figuring out another way to do this, such as calling a new method you hope to continue from your success block, and a different failure method from your failure block.
Your method can't function with its current design.
- (BOOL)whois:(NSString *)domain withZone: (NSString*) zone
{
__block NSString *resultCode;
NSURL *url = [[NSURL alloc] initWithString:#"myurl"];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
// *** Runs 1st
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
// *** runs 3rd
resultCode = [JSON valueForKeyPath:[NSString stringWithFormat:#"%#.%#", domain,zone]]; //checked with NSLog, works well
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
NSLog(#"Request Failed with Error: %#, %#", error, error.userInfo);
}];
// *** Runs 2nd
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation: operation];
[operation waitUntilFinished];
if(resultCode == #"available") //nil here
{
return YES;
}
return NO;
}
Because the material in the block runs third, and asynchronously, you won't be able to return that value to the greater method in the manner it is currently designed. Perhaps use something like this:
- (void)whois:(NSString *)domain withZone: (NSString*) zone
{
NSURL *url = [[NSURL alloc] initWithString:#"myurl"];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
__weak id weakSelf = self;
// Runs 1st
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
NSString *resultCode = [JSON valueForKeyPath:[NSString stringWithFormat:#"%#.%#", domain,zone]]; //checked with NSLog, works well
[weakSelf receivedResultCode:resultCode];
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
NSLog(#"Request Failed with Error: %#, %#", error, error.userInfo);
}];
}
- (void) receivedResultCode:(NSString *)resultCode {
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation: operation];
[operation waitUntilFinished];
if(resultCode == #"available") //nil here
{
// do #YES stuff
}
else {
// do #NO stuff
}
}
Obviously you'll have to change the design of whoever's calling it because it won't return a value in the way you specified. Perhaps there is a better solution, but I think this is the type of design required for it to work.

No Response from AFJSONRequestOperation

I'm trying to get the AFJSONRequestOperation to work with http://www.crowdkind.org/mobile/offers.
I get through the synchronous part and it returns data and puts it in the table just fine. When I go to convert it to AFJSONRequestOperation - I don't get a response. The operation is sent as the NSLog tells me that. But I never get "Operation succeeded or failed". Any idea what might be happening? Here's my searchBarButtonClicked method...
URLtoSearch is set to... http://www.crowdkind.org/mobile/offers. (feel free to click to as it's valid JSON).
- (void)searchBarSearchButtonClicked
{
searchResults = [NSMutableArray arrayWithCapacity:10];
[queue cancelAllOperations];
isLoading = YES;
[self.tableView reloadData];
NSURL *url = [self urlWithSearchText:URLtoSearch];
NSLog(#"URL: %#", URLtoSearch);
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFJSONRequestOperation *operation = [AFJSONRequestOperation
JSONRequestOperationWithRequest:request
success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
NSLog(#"Operation succeeded");
[self parseDictionary:JSON];
[searchResults sortUsingSelector:#selector(compareName:)];
isLoading = NO;
[self.tableView reloadData];
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
NSLog(#"Operation failed");
[self showNetworkError];
isLoading = NO;
[self.tableView reloadData];
}];
operation.acceptableContentTypes = [NSSet setWithObjects:#"application/json", #"text/json", #"text/javascript", nil];
[queue addOperation:operation];
NSLog(#"Operation added");
}
I get no response. Check the link, it's valid json. Any thoughts greatly appreciated!