Im trying to understand completion handlers & blocks. I believe you can use blocks for many deep programming things without completion handlers, but I think i understand that completion handlers are based on blocks. (So basically completion handlers need blocks but not the other way around).
So I saw this code on the internet about the old twitter framework:
[twitterFeed performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
if (!error) {
self.successLabel.text = @"Tweeted Successfully";
[self playTweetSound];
} else {
// Show alert
}
// Stop indicator
sharedApplication.networkActivityIndicatorVisible = NO;
}];
Here we are calling a method which does stuff (performs TWRequest) and returns when finished with responseData & urlResponse & error. Only when it returns does it execute the block which tests granted and stops the activity indicator. PERFECT!
Now this is a setup I have for a different app which works, but I'm trying to put the pieces together:
@interface
Define an ivar
typedef void (^Handler)(NSArray *users);
Declare the method
+(void)fetchUsersWithCompletionHandler:(Handler)handler;
@implementation
+(void)fetchUsersWithCompletionHandler:(Handler)handler {
//...Code to create NSURLRequest omitted...
__block NSArray *usersArray = [[NSArray alloc] init];
//A. Executes the request
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
// Peform the request
NSURLResponse *response;
NSError *error = nil;
NSData *receivedData = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
// Deal with your error
if (error) {
}
NSLog(@"Error %@", error);
return;
}
// Else deal with data
NSString *responseString = [[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding];
usersArray = [NSJSONSerialization JSONObjectWithData:[responseString dataUsingEncoding:NSASCIIStringEncoding] options:0 error:nil];
// Checks for handler & returns usersArray to main thread - but where does handler come from & how does it know to wait tip usersArray is populated?
if (handler){
dispatch_sync(dispatch_get_main_queue(), ^{
handler(usersArray);
});
}
});
}
Here is my understanding:
- fetchUsersWithCompletionHandler is obviously the homologue of performRequestWithHandler
- unfortunately this is a little more complex because there is a GCD call in the way...
But basically, the request is executed and the error is dealt with, the data is dealt with and then, the handler is checked. My question is, how does this handler part work? I understand if it exists then it will send back to the main queue and return the usersArray. But how does it know to wait until usersArray is populated? I guess whats confusing me is that fact that the method:block in this case has another block inside of it, the dispatch_async call. I guess what Im looking for is the logic that actually does stuff and knows WHEN to return the responseData and urlResponse. I know its not the same app, but I cant see the code for performRequestWithHandler.