yannisalexiou
yannisalexiou

Reputation: 697

Wait for async task to finish if you know that it's called n times

In this project I use Foursquare API to get some venues and one photo from each venue. When I have those data, I'm reloading the TableView to display all the info.

First I collect the venues and secondly for each venue, I m taking a photo from foursquare API.

I m using the RestKit library for this project and I'm calling this method n times (one time for each venue). When it's finishing I want to display all those photos I have taken to my table view.

- (void)requestVenuePhoto:(Venue *)thisVenue{
  //...
  //...
  [[RKObjectManager sharedManager] getObjectsAtPath:objectPath parameters:queryParams success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult)
  {
    [self.photos addObjectsFromArray:mappingResult.array];
    //[self.tableView reloadData];

  } failure:^(RKObjectRequestOperation *operation, NSError *error) {
    NSLog(@"What do you mean by 'there is no photos?': %@", error);
    dispatch_group_leave(resolveVenuePhotos);
  }]; 
}

The problem is that I can't use the dispatch_group_leave because is'not called one time only. Is there any way to do this nicely?

Update, now I'm using a counter to solve the problem:

[[RKObjectManager sharedManager] getObjectsAtPath:objectPath parameters:queryParams success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult)
{
    [self.photos addObjectsFromArray:mappingResult.array];
    venuesPhotoCounter++;
    if (venuesPhotoCounter == _venues.count)
    {
        [self.tableView reloadData];
    }

} failure:^(RKObjectRequestOperation *operation, NSError *error) {
    NSLog(@"What do you mean by 'there is no photos?': %@", error);
}];

Upvotes: 1

Views: 299

Answers (2)

John Bassos
John Bassos

Reputation: 308

Apple has uploaded a project called "LazyTableImages" available here . So I wrapped it up and made some paradigms which should probably fit your use-case.

 - (void)startDownload
    {
        NSURLRequest *request = [NSURLRequest requestWithURL:self.venue.imageURL];


        // create an session data task to obtain and download the app icon
        _sessionTask = [[NSURLSession sharedSession] dataTaskWithRequest:request
                                                       completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {

            // in case we want to know the response status code
            NSInteger HTTPStatusCode = [(NSHTTPURLResponse *)response statusCode];


            if (error != nil)
            {
                if ([error code] == NSURLErrorAppTransportSecurityRequiresSecureConnection)
                {
                    // if you get error NSURLErrorAppTransportSecurityRequiresSecureConnection (-1022),
                    // then your Info.plist has not been properly configured to match the target server.
                    //
                    abort();
                }
            }

            [[NSOperationQueue mainQueue] addOperationWithBlock: ^{

                // Set appIcon and clear temporary data/image
                UIImage *image = [[UIImage alloc] initWithData:data];
                if (HTTPStatusCode == 200) {
                    if (image.size.width != kAppIconSize || image.size.height != kAppIconSize)
                    {
                        CGSize itemSize = CGSizeMake(kAppIconSize, kAppIconSize);
                        UIGraphicsBeginImageContextWithOptions(itemSize, NO, 0.0f);
                        CGRect imageRect = CGRectMake(0.0, 0.0, itemSize.width, itemSize.height);
                        [image drawInRect:imageRect];
                        self.venue.image = UIGraphicsGetImageFromCurrentImageContext();
                        UIGraphicsEndImageContext();
                    }
                    else
                    {
                        self.venue.image = image;
                    }
                }
                else {// If anything goes wrong we should use a placeholder image
                    self.venue.image = [UIImage imageNamed:@"Placeholder.png"];
                }



                // call our completion handler to tell our client that our icon is ready for display
                if (self.completionHandler != nil)
                {
                    self.completionHandler();
                }
            }];
        }];

        [self.sessionTask resume];
    }

Upvotes: 1

jcaron
jcaron

Reputation: 17710

This is not a direct answer to your question, but I think it will resolve your issue.

You should not load all the pictures and only then reload the table. You should:

  • load the list of items, and in your completion handler, reload the table (make sure the reload happens on the main thread if you completion handler runs on a background thread).

  • then, in the tableView:cellForItemAtIndexPath: method of your tableview datasource, start loading the image for the requested item. Also make a note of the current indexPath (often just the row), for instance in the tag of the cell, or in a custom cell property. In the completion handler for that request, decode the image (in the background), then assign it to your cell (on the main thread), after having check that the cell is still for the same indexPath (i.e. the cell has not been reused).

Example using standard NSURLSession (based on a single section, and data being in an _items instance variable):

- tableView:(UITableView *)tableView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    MyItem *item = _items[indexPath.row];
    MyCustomCell *cell = (MyCustomCell *)[tableView dequeueReusableCellWithIdentifier:@"whatever"];
    // configure the rest of the cell: labels, etc.
    cell.tag = indexPath.row;
    [[[NSURLSession sharedSession] dataTaskWithURL:item.url
                                 completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
    {
        // Remember this runs on a background thread
        if (cell.tag == indexPath.row && !error && data)
        {
            UIImage *image = [UIImage imageWithData:data];
            dispatch_async(dispatch_get_main_queue(),^
            {
                cell.myImageView.image  = image;
            });
        }
    }] resume];
}

Upvotes: 1

Related Questions