-4

Here's what I'm doing: I'm processing a login that requires manipulating the data afterwards then fires off a new view. I want to do an async request since the server isn't always immediately responsive (and I don't want a crash because I held up the main thread with a synchronous connection)

This is what it looks like now:

[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *dataReturn, NSData *data, NSError *error)
 {
    //blah blah working code


    //once data is received, it's processed and I want to call a new view
         viewController.hidesBottomBarWhenPushed = YES;
         viewController.name = returnUserData[0];
         viewController.userID = returnUserData[1];
         viewController.role = returnUserData[2];
         viewController.sID = returnUserData[3];
         [self.navigationController pushViewController:viewController animated:YES];
         NSLog(@"Pushed new view controller.");//*/

 }];//end async request

Now my problem is that it's not actually visually pushing the view. I can see my NSLog responses are working correctly (the new view immediately responds with "Hello, [name]"), but visually nothing is showing up - This is a problem.

That's fine though, I decided to instead separate the code and try to run the view transition on the main thread

This is an adaptation of I've seen posted online:

NSLog(@"init NSURL response");

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSLog(@"Begin async request");
    [NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *dataReturn, NSData *data, NSError *error)
     {
         NSLog(@"inside async");
         if (error)
         {
             UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error" message:@"There was an error communicating with the server.  Please try again." delegate:nil cancelButtonTitle:@"Dismiss" otherButtonTitles:nil];
             [alert show];
             [alert release];
             checkField.text = @"";
         }
         else
         {
             //NSLog(@"raw: %@ - filtered: %@",dataReturn, (NSString *)dataReturn);
             NSDictionary *response = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
             rawJSONData = [response objectForKey:@"d"];
             NSLog(@"Set jsondata");
         }
         NSLog(@"ended async");
     }];//end async request
    NSLog(@"Beginning main thread work");
    dispatch_sync(dispatch_get_main_queue(), ^{
       //not important for the example

So what it's doing is giving me this:

2014-03-28 21:53:37.059 myApp[652:60b] init NSURL response

2014-03-28 21:53:37.059 myApp[652:3507] Begin async request

2014-03-28 21:53:37.059 myApp[652:3507] Beginning main thread work

It's skipping over the embedded async request entirely.

So what I'm left with is two off-the-main-thread-solutions that aren't doing what I want: I want to run NSURLConnection off the main thread, I'm getting back JSON data I need to parse, and I want to wait until I get & parse that data before transitioning views.

Is that I'm trying to do possible? Or are my eyes glazed over and I'm just not seeing something I should be?

user1118321
  • 25,567
  • 4
  • 55
  • 86

1 Answers1

0

In your first example, you can probably fix it by either using [NSOperationQueue mainQueue] for the queue rather than what I presume is some background queue. Or dispatch your push to the main queue. UI updates always should be done on the main queue.

A couple of thoughts regarding your second example:

  1. You don't have to dispatch your sendAsynchronousRequest request to a background queue, because it will do it asynchronously already. Wrapping it in a dispatch_async is redundant.

  2. If you want to initiate some transition to the next view once you've successfully parsed the JSON, then put that code to transition to the next view inside the completionHandler block, right where you parse the JSON, not after the sendAsynchronousRequest.

  3. You can use your queue object if you want, but then make sure you dispatch your UIAlertView and your "transition to view controller" code back to the main queue (because all UI updates should take place on the main queue). Frankly, it's easier to just specify the mainQueue as the queue for the completionHandler (because nothing you have there is so time consuming as to warrant the use of a background queue).

  4. Merely as a matter of convention, I'd suggest leaving the NSURLResponse object called response, and rename that other variable.

  5. You might want to include more error checking (e.g. was the JSON successfully parsed). There are many server errors that would not result in the NSError object of the completionHandler to be set (e.g. that's just for basic internet connectivity problems, not random server problems).

Thus, this yields:

NSLog(@"Issuing asynchronous request");
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
 {
     NSLog(@"Received response");
     if (error)
     {
         UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error" message:@"There was an error communicating with the server.  Please try again." delegate:nil cancelButtonTitle:@"Dismiss" otherButtonTitles:nil];
         [alert show];
         [alert release];
         checkField.text = @"";
     }
     else
     {
         //NSLog(@"raw: %@ - filtered: %@",dataReturn, (NSString *)dataReturn);
         NSError *parseError = nil;
         NSDictionary *responseObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:&parseError];
         if (responseObject) 
         {
             rawJSONData = [responseObject objectForKey:@"d"];
             NSLog(@"Set jsondata");

             // initiate the transition to the new view controller here
         }
         else 
         {
             NSLog(@"JSON parse failed: %@", parseError);
             NSLog(@"string from server: %@", [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]);
         }
     }
     NSLog(@"ended async");
 }];//end async request

Clearly, you should be checking the response and that the JSON parsing

Rob
  • 415,655
  • 72
  • 787
  • 1,044