Rafi
Rafi

Reputation: 1922

iOS - Reload UITableView AFTER AFNetworking Asynchronous Image Download Threads Done

I'm trying to use AFNetworking to asynchronously download images from URLs instead of doing it one by one in my app where when you open it, the main view gets all your friends and their profile pictures and loads a UITableView with your friends' names, usernames, and profile pictures in each cell. This is the code I'm using:

[auth.loggedInUser getFriendsSuccessCallback:^(NSArray* friends){
    NSMutableArray *profilePictureRequests = [[NSMutableArray alloc] init];
    for (User* friend in friends) {
        if (![weakSelf.friendImageDictionary.allKeys containsObject:friend.userName])
        {
            if (!friend.profilePicture) {
                UIImage *profilePicture = [UIImage imageNamed:@"ProfilePic"];
                [weakSelf.friendImageDictionary setObject:profilePicture forKey:friend.userName];
            }
            else
            {
                NSURL *profilePictureURL = [NSURL URLWithString:friend.profilePicture];
                NSURLRequest *urlRequest = [NSURLRequest requestWithURL:profilePictureURL];
                AFHTTPRequestOperation *requestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:urlRequest];
                requestOperation.responseSerializer = [AFImageResponseSerializer serializer];
                [requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, UIImage* profilePicture) {
                    NSLog(@"Response: %@", profilePicture);
                    if (profilePicture == nil)
                    {
                        profilePicture = [UIImage imageNamed:@"ProfilePic"];
                    }
                    [weakSelf.friendImageDictionary setObject:profilePicture forKey:friend.userName]; 
                } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                    NSLog(@"Image error: %@", error);
                }];
                [profilePictureRequests addObject:requestOperation];
            }
        }
    }
    for (AFHTTPRequestOperation *requestOperation in profilePictureRequests)
    {
        [requestOperation start];
    }
    dispatch_async(dispatch_get_main_queue(), ^{
        [weakSelf.tableView reloadData];
        weakSelf.dashboardViewController.loadingCoverView.hidden = YES;
        [weakSelf.dashboardViewController.tableActivityIndicator stopAnimating];
        NSLog(@"FINISHED LOADING ALL PROFILE PICTURES");
    });
}

The problem is that my UITableView is being reloaded BEFORE all the image downloading background threads are finished, which results in UITableViewCells with only some profile pictures in there. How would I be able to wait for all of them to finish so I can then reload the UITableView?

Also, I'm paranoid whether I'm doing this correctly. Is the way I'm doing it (making about 30 image downloading background threads happen at the same time in this case) not good practice with AFNetworking? Will it lead to future problems if, let's say, a user has over 100 friends and there are over 100 image downloading background threads happening at the same time?

Upvotes: 2

Views: 673

Answers (2)

dloomb
dloomb

Reputation: 1972

Reloading the whole TableView could lead to some issues. You should just update the cell's ImageView when it comes onto screen. This is called Lazy Loading and saves you having to download images that may never be seen.

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    static NSString *identifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier forIndexPath:indexPath];

    NSInteger currentTag = cell.tag + 1;
    cell.tag = currentTag;

    User *user = self.friends[indexPath.row];

    NSURL *profilePictureURL = [NSURL URLWithString:user.profilePicture];
    NSURLRequest *urlRequest = [NSURLRequest requestWithURL:profilePictureURL];

    AFHTTPRequestOperation *op = [[AFHTTPRequestOperation alloc] initWithRequest:urlRequest];
    op.responseSerializer = [AFImageResponseSerializer serializer];

    [op setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, UIImage* image) {

        //Check that the cell hasn't been reused
        dispatch_async(dispatch_get_main_queue(), ^{
            if (cell.tag == currentTag) {
                [cell.imageView setImage: image];
            }
        });


    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        //Error
    }];


    return cell;
}

I can't recall off the top of my head, but AFNetworking might cache the response for you, if not, you can always right your own to save you having to download the image again.

Upvotes: 1

MrJomp
MrJomp

Reputation: 135

When in doubt use a Framework like this ones:

https://github.com/rs/SDWebImage https://github.com/path/FastImageCache

They take out all this thing that you are doing and will save you time and money to get your app to the Appstore as fast as possible!

Upvotes: 0

Related Questions