Loading JSON Asynchronously and displaying Activity Indicator View Loading JSON Asynchronously and displaying Activity Indicator View json json

Loading JSON Asynchronously and displaying Activity Indicator View


Go with option 2. It's good UI design to display things as quickly as possible, even though the UI might not be useful until the data has loaded it will at least make users of your application feel like something is happening.

Pop the UI in didFinishLaunchingWithOptions, display an activity indicator and in connectionDidFinishLoading hide and destroy the activity indicator.

I would also recommend wrapping all the async http request logic in to another class, and have it accept a delegate, then for example you could call:

{   // Show activity indicator   [httpClient get:@"www.example.com/json" withDelegate:self];}-(void)httpClientSuccessful:(NSData*)response{   // hide activity indicator}


Option 2 is certainly the correct way to go. You should never block the UI until a network operation has finished. If you have poor reception, you don't want an unresponsive app for several seconds. The user will kill it.

The NSURLConnection delegate methods are called in the order didReceiveResponse, didReceiveData (possibly several times), connectionDidFinishLoading. didFailWithError can be called any time.

I've successfully used a pattern where I immediately create and display the table view. As long as the data hasn't been loaded, I display a single table cell with an activity indicator and a text saying "Loading data...".

Update:

Here's some code (just the essential parts). The main idea is to manage the current state, either not loaded, loading, failed or ready:

@interface MyViewController : UITableViewController<NSURLConnectionDelegate>{    NSInteger state;    NSURLConnection* connection;    MyData* data;}...@end@implementation MyViewControllertypedef enum LoadingState {  eNotLoaded,  eLoading,  eFailed,  eReady} LoadingState;- (void) viewWillAppear: (BOOL) animated{  [super viewWillAppear: animated];  // Start loading the data when the table view appears for the first time  if (state == eNotLoaded) {    NSURLRequest request = // create the request    connection = [NSURLConnection initWithRequest:request delegate:self];    [request release];    state = eLoading;  }}- (void) connection: (NSURLConnection*) connection didReceiveData: (NSData*) data{  // record and process the received data}- (void) connectionDidFinishLoading: (NSURLConnection*) connection{  // parse the received data and store it into 'data'  [connection release];  connection = nil;  // state changed; redisplay the table view  state = eReady;  [[self tableView] reloadData];}- (NSInteger) tableView: (UITableView*) tableView numberOfRowsInSection: (NSInteger) section{  if (state == eReady) {    return [data numberOfRows];  } else {    return 1;  }}- (UITableViewCell*) tableView: (UITableView*) tableView cellForRowAtIndexPath: (NSIndexPath*) indexPath{  static NSString* DefaultCellIdentifier = @"Default";  UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier: DefaultCellIdentifier];  if (cell == nil) {    cell = [[[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault reuseIdentifier: DefaultCellIdentifier] autorelease];  }  if (state == eReady) {    cell.textLabel.text = [data labelAtRow: indexPath.row];   if (state == eLoading) {    cell.textLabel.text = @"Loading...";  } else {    cell.textLabel.text = @"Failed";  }  return cell;}


I have used NSURLConnection class method sendAsynchronousRequest:queue:completionHandler: which was really helpful and straight forward.

It lets me send a block as a callback for analysing error or success for the specified request:

[NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:url]                                   queue:[NSOperationQueue currentQueue]                       completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {                           if (!error) {                                [self parseData:data];                           } else {                               NSLog(@"ERROR %@", error);                           }                       }];

This way I can activate the activity indicator prior calling the method and remove it once the data is retrieved and at the same time I can handle errors.

I hope this helps ;)