I have method with two completion calls and want to call dispatch_group_notify block after that. That works after first completion, but not after second. I need to have that worked after second call too.
Here is my code:
method1WithCompletion: and method2WithCompletion: will have 2 responses, firstly that will be with local data and later that makes request for api and return api data
dispatch_group_t requestGroup = dispatch_group_create();
dispatch_group_enter(requestGroup);
[self method1WithCompletion:^(NSArray *items) {
//here I will get 2 responses (first from local storage, second from api)
dispatch_group_leave(requestGroup);
}];
dispatch_group_enter(requestGroup);
[self method2WithCompletion:^(NSArray *items) {
//here I will get 2 responses (first from local storage, second from api)
dispatch_group_leave(requestGroup);
}];
// now this will only be called once the two "enter" calls are balanced with the corresponding "leave" calls
dispatch_group_notify(requestGroup, dispatch_get_main_queue(), ^{
//completion
//that should be called after method 1 and method 2 executed with local data, and after they executed with api data
});
do not sure how to setup that properly
I understand that these completion blocks will each be called twice, once for local data, second time with API data. And you want the notify block called when the two local calls finish, and again when the two API calls finish.
To do this, you need two dispatch groups, and you could do something like:
dispatch_group_t requestGroupLocal = dispatch_group_create();
dispatch_group_t requestGroupAPI = dispatch_group_create();
dispatch_group_enter(requestGroupLocal);
dispatch_group_enter(requestGroupAPI);
__block NSInteger count1 = 0;
[self method1WithCompletion:^(NSArray *items) {
if (count1 == 0) {
dispatch_group_leave(requestGroupLocal);
count1++;
} else {
dispatch_group_leave(requestGroupAPI);
}
}];
dispatch_group_enter(requestGroupLocal);
dispatch_group_enter(requestGroupAPI);
__block NSInteger count2 = 0;
[self method2WithCompletion:^(NSArray *items) {
if (count2 == 0) {
dispatch_group_leave(requestGroupLocal);
count2++;
} else {
dispatch_group_leave(requestGroupAPI);
}
}];
dispatch_block_t completion = ^{
// do whatever you want when each group finishes
};
dispatch_group_notify(requestGroupLocal, dispatch_get_main_queue(), completion);
dispatch_group_notify(requestGroupAPI, dispatch_get_main_queue(), completion);
Or, better, rather than relying on those cryptic counters, you could add a parameter to the block to indicate whether it's a local call or not:
dispatch_group_t requestGroupLocal = dispatch_group_create();
dispatch_group_t requestGroupAPI = dispatch_group_create();
dispatch_group_enter(requestGroupLocal);
dispatch_group_enter(requestGroupAPI);
[self method1WithCompletion:^(NSArray *items, BOOL isLocal) {
if (isLocal) {
dispatch_group_leave(requestGroupLocal);
} else {
dispatch_group_leave(requestGroupAPI);
}
}];
dispatch_group_enter(requestGroupLocal);
dispatch_group_enter(requestGroupAPI);
[self method2WithCompletion:^(NSArray *items, BOOL isLocal) {
if (isLocal) {
dispatch_group_leave(requestGroupLocal);
} else {
dispatch_group_leave(requestGroupAPI);
}
}];
dispatch_block_t completion = ^{
// do whatever you want when each group finishes
};
dispatch_group_notify(requestGroupLocal, dispatch_get_main_queue(), completion);
dispatch_group_notify(requestGroupAPI, dispatch_get_main_queue(), completion);
Frankly, this pattern of calling the completion handler twice is quite atypical and is somewhat suspect. I can imagine the intent behind the current design, but I would be inclined to retire it. Instead, I'd rather see separate methods for the local and API renditions which call their completion handlers only once:
dispatch_group_t requestGroupLocal = dispatch_group_create();
dispatch_group_enter(requestGroupLocal);
[self methodLocal1WithCompletion:^(NSArray *items) {
dispatch_group_leave(requestGroupLocal);
}];
dispatch_group_enter(requestGroupLocal);
[self methodLocal2WithCompletion:^(NSArray *items) {
dispatch_group_leave(requestGroupLocal);
}];
dispatch_group_t requestGroupAPI = dispatch_group_create();
dispatch_group_enter(requestGroupAPI);
[self methodAPI1WithCompletion:^(NSArray *items) {
dispatch_group_leave(requestGroupAPI);
}];
dispatch_group_enter(requestGroupAPI);
[self methodAPI2WithCompletion:^(NSArray *items) {
dispatch_group_leave(requestGroupAPI);
}];
dispatch_block_t completion = ^{
// do whatever you want when each group finishes
};
dispatch_group_notify(requestGroupLocal, dispatch_get_main_queue(), completion);
dispatch_group_notify(requestGroupAPI, dispatch_get_main_queue(), completion);
Or, if you really wanted just one method for methods 1 and 2, then make local
a parameter, but again, write methods that call their completion handler only once:
dispatch_group_t requestGroupLocal = dispatch_group_create();
dispatch_group_enter(requestGroupLocal);
[self method1Local:true completion:^(NSArray *items) {
dispatch_group_leave(requestGroupLocal);
}];
dispatch_group_enter(requestGroupLocal);
[self method2Local:true completion:^(NSArray *items) {
dispatch_group_leave(requestGroupLocal);
}];
dispatch_group_t requestGroupAPI = dispatch_group_create();
dispatch_group_enter(requestGroupAPI);
[self method1Local:false completion:^(NSArray *items) {
dispatch_group_leave(requestGroupAPI);
}];
dispatch_group_enter(requestGroupAPI);
[self method2Local:false completion:^(NSArray *items) {
dispatch_group_leave(requestGroupAPI);
}];
dispatch_block_t completion = ^{
// do whatever you want when each group finishes
};
dispatch_group_notify(requestGroupLocal, dispatch_get_main_queue(), completion);
dispatch_group_notify(requestGroupAPI, dispatch_get_main_queue(), completion);