Reputation: 19969
This might be a bit of a naive question but in my loading ViewController, I am loading all the content that the app needs using a a set of methods like getEachItem below. This is usually like 2 or 3 items and they are all written to a cache.
I'd like to call the method 'showNavigation' that runs after the final instance of getEachItem has completed but not sure how to do this. getEachItem does a GET request with AFNetworking. Something like a jQuery complete block but for the totality of the below for loop.
NSArray *tmpItems=[result objectForKey:@"ipad_items"];
for(NSDictionary *m in tmpItems){
// will also increment into the _menus array
[self getEachItem:[m objectForKey:@"id"]];
[self getImages:[m objectForKey:@"id"]];
}
[self showNavigation];
Upvotes: 2
Views: 645
Reputation: 437582
When you call the AFNetworking GET
method, it returns a AFHTTPRequestOperation
(a NSOperation
subclass). You can leverage this fact to employ an operation queue-based solution to your problem. Namely, you can create a new "completion operation" which is dependent upon the completion of particular AFNetworking operations.
For example, you might change your getEachItem
method to return the the AFHTTPRequestOperation
returned by the GET
method. For example, let's assume you have a getEachItem
is currently defined something like:
- (void)getEachItem:(id)identifier
{
// do a lot of stuff
[self.manager GET:... parameters:... success:... failure:...];
}
Change that to:
- (NSOperation *)getEachItem:(id)identifier
{
// do a lot of stuff
return [self.manager GET:... parameters:... success:... failure:...];
}
Then, you can create your own completion operation which will be dependent upon all those other AFHTTPRequestOperation
operations finishing. Thus:
NSOperation *completion = [NSBlockOperation blockOperationWithBlock:^{
[self showNavigation];
}];
NSArray *tmpItems=[result objectForKey:@"ipad_items"];
for(NSDictionary *m in tmpItems){
// will also increment into the _menus array
NSOperation *operation = [self getEachItem:[m objectForKey:@"id"]];
[completion addDependency:operation];
[self getImages:[m objectForKey:@"id"]];
}
[[NSOperationQueue mainQueue] addOperation:completion];
Once you've done that, the completion
operation will not fire until all of the getEachItem
operations complete. Note, this completion operation will fire when the core AFHTTPRequestOperation
objects finish, but there are no assurances that their respective completion blocks of those requests are necessarily done.
Another approach is to use a GCD "group". Using this technique, you "enter" the group when you submit each request, you "leave" the group in your completion blocks of the GET
method. You can then specify a block of code to be performed when the the group notifies you that you have left the group as many times as you've entered it (i.e. all of the AFNetworking network requests, and their success
/failure
blocks, are finished).
For example, add a dispatch_group_t
parameter to getEachItem
:
- (void)getEachItem:(id)identifier group:(dispatch_group_t)group
{
dispatch_group_enter(group);
// do a lot of stuff
[self.manager GET:... parameters:... success:^(...) {
// do you success stuff and when done, leave the group
dispatch_group_leave(group);
} failure:^(...) {
// do you failure stuff and when done, leave the group
dispatch_group_leave(group);
}];
}
Note, you "enter" the group before submitting the request, and both the success
and failure
blocks must call dispatch_group_leave
.
Having done that, you can now use the dispatch_group_t
in your loop of requests, performing the showNavigation
when the group receives its notification that everything is done:
dispatch_group_t group = dispatch_group_create();
NSArray *tmpItems=[result objectForKey:@"ipad_items"];
for(NSDictionary *m in tmpItems){
// will also increment into the _menus array
[self getEachItem:[m objectForKey:@"id"] group:group];
[self getImages:[m objectForKey:@"id"]];
}
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
[self showNavigation];
});
Upvotes: 4