0

I'm trying to refactor some NSJSONSerialization code so that it is not on the main thread. At the moment the app is a bit sluggish.

I would like to refactor this code to what I have below and am having problems with the syntax particularly with the error handling. For instance if I take my existing code (the requestData: method) and put it within

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
});

the table no longer loads any data.

thanks for any help.

-(void)requestData {

    [HUD showUIBlockingIndicatorWithText:@"Fetching JSON"];

    NSError *requestError = nil;

    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL
                                                          URLWithString:kURL]];

    NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:&requestError];

    NSError *jsonParsingError = nil;

    if (requestError)
    {
        NSLog(@"sync. request failed with error: %@", requestError);
    }
    else
    {
        // handle data
       publicData =  [NSJSONSerialization JSONObjectWithData:response
                                                                    options:0
                                                                      error:&jsonParsingError];
        publicDataArray = [publicData objectForKey:@"data"];

    }

    /*
     for(publicDataDict in publicDataArray) {
     NSLog(@"data output is %@",[publicDataDict objectForKey:@"title"]);

     }
     */
    [self.mainTableView reloadData];

    [HUD hideUIBlockingIndicator];
}

Here's the code I would like to use.

-(void)viewDidAppear:(BOOL)animated
{

    [HUD showUIBlockingIndicatorWithText:@"Fetching Data"];

    //1
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //code executed in the background
        //2
        NSData* ghData = [NSData dataWithContentsOfURL:
                            [NSURL URLWithString:kURL]
                            ];
        //3
        NSDictionary* json = nil;
        if (ghData) {
            json = [NSJSONSerialization
                    JSONObjectWithData:ghData
                    options:kNilOptions
                    error:nil];
        }

        //4
        dispatch_async(dispatch_get_main_queue(), ^{
            //code executed on the main queue
            //5


            [self.tableView reloadData];
            [HUD hideUIBlockingIndicator];
        });

    });
}
hanumanDev
  • 6,592
  • 11
  • 82
  • 146
  • Please be specific about the problems, include compiler / exception logs and stack traces. Don't make us guess about what you have issues with. – Wain Jul 03 '13 at 10:00
  • @Wain I made an edit. Basically when I put the code within dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ }); the table data no longer loads – hanumanDev Jul 03 '13 at 10:06
  • Is the json actually being parsed correctly, or are you getting an error. Debug to check. – Wain Jul 03 '13 at 10:10
  • In the first code segment you use `self.mainTableView`, but in the second one you use `self.tableView` – colincameron Jul 03 '13 at 10:11
  • @Wain yes the data is outputted to the console. the table loading the data is the issue. – hanumanDev Jul 03 '13 at 10:17
  • @c.cam108 yes, I'm using the first code with self.mainTableView - which is the correct one. the second method I'm not using and want my code to look like that with the optimized dispatching – hanumanDev Jul 03 '13 at 10:18
  • So the second code isn't actually yours? Please post the code you are using that you say doesn't work. – colincameron Jul 03 '13 at 10:45

4 Answers4

2

A lot of people assume that sending a process to the background will automatically rid their application of any sluggishness, this is a WRONG assumption. If you send a CPU intensive task to the background, it is going to block the CPU as well. For multi-threading to work in your favour, you have to be methodical about it.

Now onto your problem, the simplest solution is for you to use what Apple already provides, NSURLConnection is your best bet, NEVER Use [NSData dataWithContentsOfURL:] this is a definite no no. It is not the NSJSONSerialization that is the problem, its the network request.

You have two options really.

1) Use NSRULConnection delegate method and place your JSON serialization methods in – connectionDidFinishLoading: delegate method

2) Use the block methods for NSURLConnection [NSURLConnection sendAsynchronousRequest: queue: completionHandler:] (My preferred choice)

-(void)viewDidAppear:(BOOL)animated
{
    [HUD showUIBlockingIndicatorWithText:@"Fetching Data"];

    NSURLRequest *request = [NSURLRequest requestWithURL:URL];
    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {

    if (!error) {
        NSError *jsonError = nil;
        NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&jsonError];

        if (jsonError) {
            NSLog(@"Error parsing JSON");
            //Optionally display error message here
        }else{

            self.globalDictionary = jsonDict;

            [self.tableView reloadData];
            [HUD hideUIBlockingIndicator];
        }

    }else
    {
        NSLog(@"Error with request");

        [HUD hideUIBlockingIndicator];
        //Optionally display error message here
    }


}];

}

Notes: globalDictionary is an NSDictionary instance that populates the table.

Guntis Treulands
  • 4,764
  • 2
  • 50
  • 72
Edwin
  • 3,812
  • 2
  • 21
  • 16
1

So the possible guess wold be for this is probably the table reload method will be called earlier. so at the last you can reload the table as given below.

-(void)viewDidAppear:(BOOL)animated
{

    [HUD showUIBlockingIndicatorWithText:@"Fetching Data"];

    //1
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //code executed in the background
        //2
        NSData* ghData = [NSData dataWithContentsOfURL:
                          [NSURL URLWithString:kURL]
                          ];
        //3
        NSDictionary* json = nil;
        if (ghData) {
            json = [NSJSONSerialization
                    JSONObjectWithData:ghData
                    options:kNilOptions
                    error:nil];
        }

        //4
        [self performSelectorOnMainThread:@selector(reloadTable) withObject:nil waitUntilDone:NO];

    });
}

and after this do like this.

-(void)reloadTable {

    [self.tableView reloadData];
    [HUD hideUIBlockingIndicator];
}

Also check if tableview datasource delegate methods are getting called if it is not getting called then set the UITableView delegate.

Gyanendra
  • 361
  • 2
  • 15
0

If you are refactoring your code, make use of functions. You should write a logic/code aimed at doing certain task in a separate function, and also your LOC in any function should not exceed more then 20, in normal cases.

Talking about your problem, it looks you have put it right but i am not seeing you defining the source of tableView, please check if you have converted the JSON to any container object viz dictionary or an array.

    -(void)viewDidAppear:(BOOL)animated
{
    [HUD showUIBlockingIndicatorWithText:@"Fetching Data"];

    [self fetchData];
}

-(void)fetchData
{
  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  // switch to a background thread and perform your expensive operation

  NSData* ghData = [NSData dataWithContentsOfURL:
                            [NSURL URLWithString:kURL]
                            ];

  NSDictionary* json = nil;

  if (ghData) 
  {
    json = [NSJSONSerialization
                    JSONObjectWithData:ghData
                    options:kNilOptions
                    error:nil];
   }

   dispatch_async(dispatch_get_main_queue(), ^{
   // switch back to the main thread to update your UI
  [self.tableView reloadData];
  [HUD hideUIBlockingIndicator];
    });

 });
}
Say2Manuj
  • 709
  • 10
  • 20
-3

Try first in to refactor to convert the project for new architecture ARC, i post this in a old answer take a look here:

My Post

Hope this help you or give a idea to refactor your code ;)

Community
  • 1
  • 1
BlackSheep
  • 1,087
  • 12
  • 29