Search code examples
iosobjective-cjsonuitableviewuirefreshcontrol

UIRefreshControl with two async json downloads to populate UITableView rows and custom header view


I've got an UITableViewController that fetches it's info from a WS json working fine.

Now I need to populate both the table and a Table's header view with two different WS calls, on a UIRefreshControl pull.

-(void)refreshView:(UIRefreshControl *)refresh {        
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

            __block NSData *data = [self fetchNewData];
            dispatch_async(dispatch_get_main_queue(), ^{
                if (data) {
                    [self fetchedData:data];
                    [self.tableView reloadData];
                } else {
                    //show error    
                }

                [self.refreshControl endRefreshing];
            });
    });
}


-(NSData*)fetchNewData {
    NSString *api = SINGLE URL;
    if (api) {
        NSData* data = [NSData dataWithContentsOfURL:
                        [NSURL URLWithString:api]];
        return data;
    } else {
        return nil; //alert
    }
}

- (void)fetchedData:(NSData *)responseData {
    //parse out the json data
    NSError* error = nil;
        _JSONDict = [NSJSONSerialization
                     JSONObjectWithData:responseData

                     options:kNilOptions
                     error:&error];

        _tableDataRows = [_JSONDict objectForKey:@"tableDataRows"];    
    }
}

Finally, - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath uses _tableDataRows to populate the table.

Now, i want to update some labels on the Table's header view, which is a custom view from a ViewController, from a second API URL json, on the same "pull" of the UIRefreshControl, meaning that the loading animation should be on until both jsons are downloaded and parsed. What would be the best way?

If you need more info just let me know.. Thanks!


Solution

  • After reading @Greg's commented link and this other Q: Waiting until two async blocks are executed before starting another block

    I came up with this which is working the way I want:

    -(void)refreshView:(UIRefreshControl *)refresh {
    
    dispatch_group_t group = dispatch_group_create();
    __block BOOL isReachable = NO;
    __block NSData *data = nil;
    __block NSData *dataPublic = nil;
    
    dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ {
        isReachable = [self.appDelegate isReachable];
    });
    
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    
    dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ {
        if (isReachable) {
            data = [self fetchNewData];
        }
    });
    
    dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ {
        if (isReachable) {
            dataPublic = [self fetchPublicData];
        }
    });
    
    dispatch_group_notify(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ {
        if (isReachable) {
            dispatch_async(dispatch_get_main_queue(), ^{
                //Public data
                if (dataPublic) {
                    [self fetchedPublicData:dataPublic];
                } else {
                    //handle error                    
                }
                //User data
                if (data) {
                    [self fetchedData:data];
                    [self.tableView reloadData];
                } else {
                    //handle error                    
                }
    
                [self.refreshControl endRefreshing];
            });
        } else {
            dispatch_async(dispatch_get_main_queue(), ^{
                [self.refreshControl endRefreshing];
                [self.appDelegate showConnectionErrorAlert];
            });
        }
    
    });
    
    }
    

    Please feel free to suggest improvements!