When using methods which return blocks they can be very convenient. However, when you have to string a few of them together it gets messy really quickly
for instance, you have to call 4 URLs in succession:
[remoteAPIWithURL:url1 success:^(int status){
[remoteAPIWithURL:url2 success:^(int status){
[remoteAPIWithURL:url3 success:^(int status){
[remoteAPIWithURL:url2 success:^(int status){
//succes!!!
}];
}];
}];
}];
So for every iteration I go one level deeper, and I don't even handle errors in the nested blocks yet.
It gets worse when there is an actual loop. For instance, say I want to upload a file in 100 chunks:
- (void) continueUploadWithBlockNr:(int)blockNr
{
if(blocknr>=100)
{
//success!!!
}
[remoteAPIUploadFile:file withBlockNr:blockNr success:^(int status)
{
[self continueUploadWithBlockNr:blockNr];
}];
}
This feels very unintuitive, and gets very unreadable very quick.
In .Net they solved all this using the async and await keyword, basically unrolling these continuations into a seemingly synchronous flow.
What is the best practice in Objective C?
Your question immediately made me think of recursion. Turns out, Objective-c blocks can be used in recursion. So I came up with the following solution, which is easy to understand and can scale to N tasks pretty nicely.
// __block declaration of the block makes it possible to call the block from within itself
__block void (^urlFetchBlock)();
// Neatly aggregate all the urls you wish to fetch
NSArray *urlArray = @[
[NSURL URLWithString:@"http://www.google.com"],
[NSURL URLWithString:@"http://www.stackoverflow.com"],
[NSURL URLWithString:@"http://www.bing.com"],
[NSURL URLWithString:@"http://www.apple.com"]
];
__block int urlIndex = 0;
// the 'recursive' block
urlFetchBlock = [^void () {
if (urlIndex < (int)[urlArray count]){
[self remoteAPIWithURL:[urlArray objectAtIndex:index]
success:^(int theStatus){
urlIndex++;
urlFetchBlock();
}
failure:^(){
// handle error.
}];
}
} copy];
// initiate the url requests
urlFetchBlock();