Daljeet
Daljeet

Reputation: 1595

How to display UIActivityIndicatorView while fetching JSON data asynchronously to be populated in UITableView?

I am calling a GET API , that takes a string keyword and returns me JSON data , which i parse and display in my UITableView

While the API returns data , I am displaying UIActivityIndicatorView, this is working fine. However , as soon as the data is recieved the UIActivityIndicatorView disappears as expected but data does not show in the UITableView, but if i touch anywhere on the screen the data gets visible in the UI TableView.

This is my code:

-(void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
{
    [indicator startAnimating];
    indicator.hidesWhenStopped = YES;

    dispatch_queue_t queue = dispatch_queue_create("ID", NULL);
    dispatch_async(queue, ^{
        NSString *searchText=searchBar.text;
        NSString *trimmedString = [searchText stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceCharacterSet]];
        if (trimmedString.length==0) {
            isFilter=NO;

            UIAlertView *noConn = [[UIAlertView alloc] initWithTitle:@"ERROR" message:@"Please enter something in search bar" delegate:self cancelButtonTitle:nil otherButtonTitles:@"ok", nil];
            [noConn show];
        } else {
            NSString *searchNew = [trimmedString stringByReplacingOccurrencesOfString:@" " withString:@"%20"];
            isFilter=YES;
            @try {
                [label removeFromSuperview];
                _Title1 = [[NSMutableArray alloc] init];
                _Author1 = [[NSMutableArray alloc] init];
                _Images1 = [[NSMutableArray alloc] init];
                _Details1 = [[NSMutableArray alloc] init];
                _link1 = [[NSMutableArray alloc] init];
                _Date1 = [[NSMutableArray alloc] init];
                NSString* myURLString = [NSString stringWithFormat:@"www.example.com=%@", searchNew];
                NSURL *url = [NSURL URLWithString:myURLString];

                NSData* data = [NSData dataWithContentsOfURL:url];

                if ((unsigned long)data.length > 3) {
                    NSArray *ys_avatars = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
                    if(ys_avatars) {
                        for (int j=0;j<ys_avatars.count;j++) {
                            if( ys_avatars[j][@"title"]==[NSNull null] ) {
                                [_Title1 addObject: @""];
                            } else {
                                [_Title1 addObject:ys_avatars[j][@"title"]];
                            }

                            if( ys_avatars[j][@"author"]==[NSNull null] ) {
                                [_Author1 addObject: @""];
                            }

                            [_Author1 addObject: ys_avatars[j][@"author"]];

                            if( ys_avatars[j][@"featured_img"]==[NSNull null] ) {
                                [_Images1 addObject: @""];
                            } else {
                                [_Images1 addObject: ys_avatars[j][@"featured_img"]];

                            }

                            if( ys_avatars[j][@"content"]==[NSNull null] ) {
                                [_Details1 addObject: @""];
                            } else {
                                [_Details1 addObject:ys_avatars[j][@"content"]];
                            }

                            if( ys_avatars[j][@"permalink"]==[NSNull null] ) {
                                [_link1 addObject: @""];
                            } else {
                                [_link1 addObject:ys_avatars[j][@"permalink"]];
                            }

                            if( ys_avatars[j][@"date"]==[NSNull null] ) {
                                [_Date1 addObject: @""];
                            } else {
                                NSString *newStr=[ys_avatars[j][@"date"] substringToIndex:[ys_avatars[j][@"date"] length]-3];
                                [_Date1 addObject:newStr];
                            }
                        }
                    } else {
                        NSLog(@"error");
                    }
                    [self.myTableView reloadData];
                } else {
                    if(IDIOM == IPAD){
                        [self.myTableView reloadData];

                        self.tableView.separatorColor = [UIColor colorWithRed:255/255.0 green:255/255.0 blue:255/255.0 alpha:1.0];

                        label = [[UILabel alloc] initWithFrame:CGRectMake(150, 200, 200, 100)];
                        label.text=@"No Article Found";
                        label.backgroundColor = [UIColor clearColor];
                        [self.view addSubview:label];
                    } else {
                        [self.myTableView reloadData];
                        self.tableView.separatorColor = [UIColor colorWithRed:255/255.0 green:255/255.0 blue:255/255.0 alpha:1.0];
                        label = [[UILabel alloc] initWithFrame:CGRectMake(90, 100, 200, 100)];
                        label.text=@"No Article Found";
                        label.backgroundColor = [UIColor clearColor];
                        [self.view addSubview:label];
                    }
                }
            }
            @catch (NSException *exception) { }
        }
        dispatch_async(dispatch_get_main_queue(), ^{
            [indicator performSelectorOnMainThread:@selector(stopAnimating) withObject:nil waitUntilDone:YES];
        });
    });

    [self.mySearchBar resignFirstResponder];
}

Upvotes: 1

Views: 430

Answers (2)

Mike S
Mike S

Reputation: 42325

Your basic problem is that you are trying to update the UI from background threads. All UI updates must be done on the main thread / queue.

Usually the easiest way to do that is by using:

dispatch_async(dispatch_get_main_queue(), ^{
    // code to run on the main queue
});

I actually see that you're using that when you stop the UIActiviteIndicatorView here:

dispatch_async(dispatch_get_main_queue(), ^{
    [indicator performSelectorOnMainThread:@selector(stopAnimating) withObject:nil waitUntilDone:YES];
});

But, in that case, you're actually dispatching the stopAnimating method to the main queue twice. You only really need this:

dispatch_async(dispatch_get_main_queue(), ^{
    [indicator stopAnimating];
});

As for your table not updating, that's because you need to dispatch all your reloadData calls to the main queue.

There are quite a few places in your code that need to be dispatched back to the main queue but, instead of wrapping all of those in a dispatch_async to the main queue, there's an easier way. The only place I see where you are actually doing something that should be done on a background thread is this line:

NSData* data = [NSData dataWithContentsOfURL:url];

Which means you can get rid of the dispatch_async(queue, ^{...}); at the beginning of your method and, instead, only do that just before you call [NSData dataWithContentsOfUrl:url]. Then, dispatch_async back to the main queue immediately after.

Like this:

-(void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
{
    [indicator startAnimating];
    indicator.hidesWhenStopped = YES;

    NSString *searchText=searchBar.text;
    NSString *trimmedString = [searchText stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceCharacterSet]];
    if (trimmedString.length==0) {
        isFilter=NO;

        UIAlertView *noConn = [[UIAlertView alloc] initWithTitle:@"ERROR" message:@"Please enter something in search bar" delegate:self cancelButtonTitle:nil otherButtonTitles:@"ok", nil];
        [noConn show];
    } else {
        NSString *searchNew = [trimmedString stringByReplacingOccurrencesOfString:@" " withString:@"%20"];
        isFilter=YES;
        @try {
            [label removeFromSuperview];
            _Title1 = [[NSMutableArray alloc] init];
            _Author1 = [[NSMutableArray alloc] init];
            _Images1 = [[NSMutableArray alloc] init];
            _Details1 = [[NSMutableArray alloc] init];
            _link1 = [[NSMutableArray alloc] init];
            _Date1 = [[NSMutableArray alloc] init];
            NSString* myURLString = [NSString stringWithFormat:@"www.example.com=%@", searchNew];
            NSURL *url = [NSURL URLWithString:myURLString];

            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                NSData* data = [NSData dataWithContentsOfURL:url];

                dispatch_async(dispatch_get_main_queue(), ^{
                    if ((unsigned long)data.length > 3) {
                        NSArray *ys_avatars = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
                        if(ys_avatars) {
                            for (int j=0;j<ys_avatars.count;j++) {
                                if( ys_avatars[j][@"title"]==[NSNull null] ) {
                                    [_Title1 addObject: @""];
                                } else {
                                    [_Title1 addObject:ys_avatars[j][@"title"]];
                                }

                                if( ys_avatars[j][@"author"]==[NSNull null] ) {
                                    [_Author1 addObject: @""];
                                }

                                [_Author1 addObject: ys_avatars[j][@"author"]];

                                if( ys_avatars[j][@"featured_img"]==[NSNull null] ) {
                                    [_Images1 addObject: @""];
                                } else {
                                    [_Images1 addObject: ys_avatars[j][@"featured_img"]];

                                }

                                if( ys_avatars[j][@"content"]==[NSNull null] ) {
                                    [_Details1 addObject: @""];
                                } else {
                                    [_Details1 addObject:ys_avatars[j][@"content"]];
                                }

                                if( ys_avatars[j][@"permalink"]==[NSNull null] ) {
                                    [_link1 addObject: @""];
                                } else {
                                    [_link1 addObject:ys_avatars[j][@"permalink"]];
                                }

                                if( ys_avatars[j][@"date"]==[NSNull null] ) {
                                    [_Date1 addObject: @""];
                                } else {
                                    NSString *newStr=[ys_avatars[j][@"date"] substringToIndex:[ys_avatars[j][@"date"] length]-3];
                                    [_Date1 addObject:newStr];
                                }
                            }
                        } else {
                            NSLog(@"error");
                        }
                        [self.myTableView reloadData];
                    } else {
                        if(IDIOM == IPAD){
                            [self.myTableView reloadData];

                            self.tableView.separatorColor = [UIColor colorWithRed:255/255.0 green:255/255.0 blue:255/255.0 alpha:1.0];

                            label = [[UILabel alloc] initWithFrame:CGRectMake(150, 200, 200, 100)];
                            label.text=@"No Article Found";
                            label.backgroundColor = [UIColor clearColor];
                            [self.view addSubview:label];
                        } else {
                            [self.myTableView reloadData];
                            self.tableView.separatorColor = [UIColor colorWithRed:255/255.0 green:255/255.0 blue:255/255.0 alpha:1.0];
                            label = [[UILabel alloc] initWithFrame:CGRectMake(90, 100, 200, 100)];
                            label.text=@"No Article Found";
                            label.backgroundColor = [UIColor clearColor];
                            [self.view addSubview:label];
                        }
                    }

                    [indicator stopAnimating];
                });
            });
        }
        @catch (NSException *exception) { }
    }

    [self.mySearchBar resignFirstResponder];
}

Note: You are doing quite a bit in that one method. I'd suggest splitting that up in to multiple methods to make your code more readable and maintainable.

Upvotes: 4

B.I.A
B.I.A

Reputation: 700

try to use NSURLConnection that would save a lot of headache and make your URL requests more manageable

@interface myTableView : UITableViewController<NSURLConnectionDelegate>{
     NSMutableData *_responseData;
}

and then use the delegate methods to parse data received,stop your indicatorview,and reload your tableview

#pragma mark NSURLConnection Delegate Methods

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    // A response has been received, this is where we initialize the instance var you created
    // so that we can append data to it in the didReceiveData method
    // Furthermore, this method is called each time there is a redirect so reinitializing it
    // also serves to clear it
    _responseData = [[NSMutableData alloc] init];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    // Append the new data to the instance variable you declared
    [_responseData appendData:data];
}

- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
                  willCacheResponse:(NSCachedURLResponse*)cachedResponse {
    // Return nil to indicate not necessary to store a cached response for this connection 
    return nil;
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    // The request is complete and data has been received
    // You can parse the stuff in your instance variable now

}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    // The request has failed for some reason!
    // Check the error var
}

and make your URL request wherever you want

// Create the request.
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://google.com"]];

// Create url connection and fire request
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];

source

Upvotes: 2

Related Questions