Skip to content

Instantly share code, notes, and snippets.

@JoeMatt
Last active December 12, 2019 17:54
Show Gist options
  • Save JoeMatt/e2448c43af386ba743529c3c895750a6 to your computer and use it in GitHub Desktop.
Save JoeMatt/e2448c43af386ba743529c3c895750a6 to your computer and use it in GitHub Desktop.
Example of guarantee of parallel execution and single firing of completion block
- (void)enableFirebaseSyncCompletion:(void(^)(void))completion {
// ------- This is the setup of the groups, factories etc -----------
// Create a new top level group, __block so we can pass it into child groups
__block dispatch_group_t topGroup = dispatch_group_create();
// Type-defs make things easier to read
typedef void (^ReturnedBlock)(void);
typedef void (^ParamBlock)(ReturnedBlock);
// Make a new block called makeCompletion, that captures topGroup in it's scope
// It returns a block that calls leave on topGroup when it's inner calls are done
ReturnedBlock (^makeCompletion)() = ^ReturnedBlock(void){
// Optional: To make this more robust we could make the topGroup a parameter
// by replacig the void here `^ReturnedBlock(void)` with `^ReturnedBlock(dispatch_group_t)`
// Make new inner group
__block dispatch_group_t innerGroup = dispatch_group_create();
// Enter innerGroup once
dispatch_group_enter( innerGroup );
ReturnedBlock retBlock = ^void(void){
// Block that is returned, leaves the inner group.
// It's ok if this block gets called multiple times, because the
// group notify will fire on the first call
dispatch_group_leave( innerGroup );
};
// After the returned block is called the first time,
// it will trigger this to execute...
dispatch_group_notify(innerGroup, dispatch_get_main_queue(), ^{
// .. which leaves the topGroup 1 time, to match our 1 entry
// at the top of this makeCompletion
dispatch_group_leave( topGroup );
});
// Return the block that will trigger the innerGroup -> outerGroup leave sequence when called by whomever we give it too
return retBlock;
};
// Convenice block that handles calling the makeCompletion for us
void (^wrap)(ParamBlock) = ^(ParamBlock param){
// Make a new completion handler,
// remember, calling makeCompletion() makes a new first class function
// which it's own innerGroup, but captures the __block reference to the main topGroup
ReturnedBlock newCompletion = makeCompletion();
// This is where we actaully enter the top group
dispatch_group_enter(topGroup);
// call the passed in code block,
// we're also forwarding along the completion handlder that
// needs to be called at some point.
param(newCompletion);
};
// ----- This is where you're making your async calls. wrap() is a conveneince,
// ----- you could call makeCompletion() yourself and pass the returned black to wherever
// 1. example usage
wrap( ^(ReturnedBlock wrapCompletion){
// NOTE: completion can be called multiple times, but needs to be called at least once!
// You could wrap a timer here to call it after a certain time out if you wanted
[self someAsyncCall:wrapCompletion];
});
// 2. example usage
wrap( ^(ReturnedBlock wrapCompletion){
// another call example..
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// ... DO some work on the background
// ...
wrapCompletion();
});
});
// 3. example usage
ReturnedBlock anotherCompletion = makeCompletion();
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// ... Do some other work on the background
// ... ,, call the completion though!
anotherCompletion();
});
// ---- Bottom of call ----
// wait for topGroup to finish, call the passed in completion when it does
// This is async so it will return this function after
dispatch_group_notify(topGroup, dispatch_get_main_queue(), completion);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment