This is my nested block, please take have a look :
- (void)getVideoList:(NSDictionary*)videoData
completionBlock:(void (^)(NSMutableArray *))
completionBlock {
NSArray *videos = (NSArray*)[videoData objectForKey:@"items"];
NSMutableArray* videoList = [[NSMutableArray alloc] init];
for (NSDictionary *videoDetail in videos) {
if (videoDetail[@"id"][@"videoId"]){
[self initializeDictionary:videoDetail completionBlock:^(YoutubeVideo * utubeVideo) {
[videoList addObject:utubeVideo];
// NSLog(@"zuuudo %@", utubeVideo.channelProfileImageURL);
}];
}
}
completionBlock(videoList);
}
- (void)initializeDictionary:(NSDictionary *)dictionary completionBlock:(void (^)(YoutubeVideo *))
completionBlock {
YoutubeVideo *youtubeVideo = [[YoutubeVideo alloc] init];
youtubeVideo.videoTitle = dictionary[@"snippet"][@"title"];
youtubeVideo.videoID = dictionary[@"id"][@"videoId"];
youtubeVideo.channelID = dictionary[@"snippet"][@"channelId"];
[self getChannelProfilePictureForChannelID:youtubeVideo.channelID completionBlock:^(NSMutableArray *channelList) {
NSLog(@"[channelList objectAtIndex:0] %@", [channelList objectAtIndex:0]);
youtubeVideo.channelProfileImageURL = [channelList objectAtIndex:0];
}];
youtubeVideo.channelTitle = dictionary[@"snippet"][@"channelTitle"];
youtubeVideo.videoDescription = dictionary[@"snippet"][@"description"];
youtubeVideo.pubDate = [self dateWithJSONString:dictionary[@"snippet"][@"publishedAt"]];
youtubeVideo.thumbnailURL = dictionary[@"snippet"][@"thumbnails"]
[@"high"][@"url"];
completionBlock(youtubeVideo);
}
- (void)getChannelProfilePictureForChannelID:(NSString*)channelID completionBlock:(void (^)(NSMutableArray *))completionBlock
{
NSString *URL = [NSString stringWithFormat:@"https://www.googleapis.com/youtube/v3/channels?part=snippet&fields=items/snippet/thumbnails/default&id=%@&key=%@", channelID, apiKey];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:[URL stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]]];
NSURLSession *session = [NSURLSession sharedSession];
[[session dataTaskWithRequest:request
completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
if (!error){
[self getChannelProfileImageList:[NSJSONSerialization JSONObjectWithData:data options:0 error:nil] completionBlock:
^(NSMutableArray * channelList) {
// return the final list
completionBlock(channelList);
}];
}
else {
// TODO: better error handling
NSLog(@"error = %@", error);
}
}] resume];
}
- (void)getChannelProfileImageList:(NSDictionary*)channelData
completionBlock:(void (^)(NSMutableArray *))
completionBlock {
NSArray *channels = (NSArray*)[channelData objectForKey:@"items"];
NSMutableArray *channelList = [[NSMutableArray alloc] init];
for (NSDictionary *channelDetail in channels) {
[self initializeDictionaryForChannelProfileImage:channelDetail completionBlock:^(NSString *chnlProfileImageURL) {
[channelList addObject:chnlProfileImageURL];
}];
//[channelList addObject:[self initializeDictionaryForChannelProfileImage:channelDetail]];
//[channelList addObject:[[YoutubeVideo alloc] initWithDictionaryForChannelProfileImage:channelDetail]];
}
completionBlock(channelList);
}
- (void)initializeDictionaryForChannelProfileImage:(NSDictionary *)dictionary completionBlock:(void (^)(NSString *))
completionBlock
{
_channelProfileImageURL = dictionary[@"snippet"][@"thumbnails"]
[@"default"][@"url"];
completionBlock(_channelProfileImageURL);
}
Problem is in this - (void)initializeDictionary:(NSDictionary *)dictionary completionBlock:(void (^)(YoutubeVideo *))
completionBlock {
} block, has the below block
[self getChannelProfilePictureForChannelID:youtubeVideo.channelID completionBlock:^(NSMutableArray *channelList) {
NSLog(@"[channelList objectAtIndex:0] %@", [channelList objectAtIndex:0]);
youtubeVideo.channelProfileImageURL = [channelList objectAtIndex:0];
}];
Where these line of code is not executing when the block return value NSSting value.
youtubeVideo.channelProfileImageURL = _channelProfileImageURL;
NSLog(@"youtubeVideo.channelProfileImageURL %@", youtubeVideo.channelProfileImageURL);
It is getting called after executing rest of the code:
youtubeVideo.channelTitle = dictionary[@"snippet"][@"channelTitle"];
youtubeVideo.videoDescription = dictionary[@"snippet"][@"description"];
youtubeVideo.pubDate = [self dateWithJSONString:dictionary[@"snippet"][@"publishedAt"]];
youtubeVideo.thumbnailURL = dictionary[@"snippet"][@"thumbnails"]
[@"high"][@"url"];
So the value is not inserting in my object model.
Please give me a suggestion. Thanks in advance.
Have a good day.
You are mixing up asynchronous execution with an expectation that code will be executed synchronously:
This is a typical declaration for an asynchronous method where the
completionBlockargument should be called asynchronously after the all the work ofinitializeDictionaryhas been completed.Three synchronous assignments.
This is a nested call to another asynchronous method, which will call its completion block after it has finished. At the point it returns it probably has not yet called its competition block.
Four more synchronous assignments...
And then you call the completion block of
initializeDictionary:before you know thatgetChannelProfilePictureForChannelID:has completed and called its completion block.If you are writing an asynchronous method which itself needs to call an asynchronous method then you have to complete your method in the nested asynchronous method's completion...
Yes that's a bit confusing in words! Let's rearrange your method:
Do all the synchronous assignments first, then do the nested asynchronous call:
At this point the completion block of
getChannelProfilePictureForChannelIDhas done what you want it to, now do any remaining work thatinitializeDictionary:needs to do aftergetChannelProfilePictureForChannelIDcompletes. This is not much in this case, just callinitializeDictionary:competition:HTH
Addendum
From your comments I think you are misunderstanding how asynchronous chaining needs to work. Let's see if the following helps.
The method you wrote has for format:
When you call this method A, B, D & E will execute in order and then the method will return. You've no idea when C will execute and there is no guarantee it will execute before E, indeed with async network calls in all probability it will not (so you're unlikely to even get accidental correctness).
To do a sequence of async operations you need to chain them via the continuation blocks. So you can change your method to:
Putting D & E into the nested completion block. Now when you call your method only A & B execute before it returns. At some later point the nested completion block is executed asynchronously and C and D are executed. Finally E is executed, the completion block of the original call, thus completing the work. You've now guaranteed correctness, E will only be executed after the nested async call has completed.
Note: What I noticed when reading your code was that block D (the set of four assignments in your code) did not seem to be required to be executed after the nested call so I rearranged your code as:
hoisting D to the top.
This chaining of asynchronous calls is fundamental when you have an async method which itself relies on another async method – at every stage you must use the completion block chain to execute code it the correct order.
HTH