Reputation: 171
I have a problem with completion block in AFNetworking, my app needs to get json data about car and use id from this json file to request another api to retrieve images before displaying all cars in uitableview. But the uitableview reload is always called before retrieving information so it displays empty table.
I have a Model.m
typedef void (^CompletionBlock)(NSArray *results);
-(void)getPhotosWithCompletionBlock: (CompletionBlock)completionBlock failureBlock: (FailureBlock)failureBlock{
NSURLRequest *request = [NSURLRequest alloc] initWithURL:[NSURL alloc] initWithString:
[NSString stringWithFormat:@"api.getPhoto"]]];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
completionBlock(JSON);
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
failureBlock(JSON);
}];
[operation start];
}
ModelViewController.m
- (void)getAllModels{
NSURLRequest *request = ...;
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
for(NSDictionary *model in JSON){
Model *model = [Model alloc] initWithId:...;
[model getPhotosWithCompletionBlock: ^(NSArray *results){
model.photos = results;
}failureBlock:^(NSError *error) {
NSLog(@"Request Failed with Error: %@, %@", error, error.userInfo);
}];
}
[_tableView reloadData];
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
NSLog(@"Request Failed with Error: %@, %@", error, error.userInfo);
}];
[operation start];
But [_tableView reload] is always called before completionBlock of getPhotos method finish so there's an empty tableView, so how to make completion block finish before calling reload tableView method? I can put reload method inside completion block but it forces tableView reload many times makes app very unresponsive, I have 1 more method to retrieve model info so I cannot put reload method inside every completion blocks. P/S: any idea for my situation without using nested afnetworking operations?
Upvotes: 0
Views: 415
Reputation: 19098
This is not an AFNetworking related question, rather a general Objective-C
or
asynchronous
question.
You may try to reload the table view whenever the images of one model have been loaded:
- (void)getAllModels{
NSURLRequest *request = ...;
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
for(NSDictionary *model in JSON){
Model *model = [Model alloc] initWithId:...;
[model getPhotosWithCompletionBlock: ^(NSArray *results){
dispatch_async(dispatch_get_main_queue(), ^{
model.photos = results;
[self.tableView reloadData];
});
}failureBlock:^(NSError *error) {
NSLog(@"Request Failed with Error: %@, %@", error, error.userInfo);
}];
}
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
NSLog(@"Request Failed with Error: %@, %@", error, error.userInfo);
}];
You likely need to fix some edge cases when an error occurs.
If there are performance issues, you need to check where they occur. One usual suspect is the creation of UIImages on the main thread. You may schedule them to another embedded async task whose completion block then forces an update of the table view, or perform this in your getPhotosWithCompletionBock
method.
You may also consider to be more explicit about which cell you need to update instead just simply sending reloadData:
which will also reload images which are already up to date.
Upvotes: 1