You may approach the problem as follows:
First define a generic completion block type. Note, param result can be anything. If it's an NSError
this signals a failure, otherwise success.
typedef void (^completion_t)(id result);
Define a block type for your asynchronous task. Your task takes an index as input, and - since it is asynchronous, it takes a completion block as last parameter:
typedef void (^unary_async_t)(int index, completion_t completion);
Your task block object can be defined as follows:
unary_async_t task = ^(int index, completion_t completion) {
[self loadDataAtIndex:index withCompletion:^(id result){
if (![result isKindOfClass:[NSError class]]) {
[self writeDataToFile:result];
result = @"OK";
}
if (completion) {
completion(result);
}
}];
};
Note: your loadDataAtIndex:withCompletion:
is asynchronous, and thus, it takes a completion block as last parameter. The completion block's parameter result is the result of the asynchronous task, namely the "array of pages" - or an NSError
object if that fails.
In this completion block, you safe your result (the pages) to disk, invoking writeDataToFile:
. (We assuming this won't fail). If that is all finished, task invokes the provided completion block completion (if not nil) passing the result of the whole operation, which is @"OK" in case of success, and an NSError
in case of failure.
Now, the more interesting part: how to make this in a loop, where numerous _task_s will be executed sequentially, one after the other:
We define two helper methods - or functions:
What we finally need is a function with this signature:
void apply_each_with_range(int lowerBound, int upperBound,
unary_async_t task, completion_t completion);
This is the function which shall invoke task N times, passing it parameter index in the range from lowerBound (including) to upperBound (not including), where N equals upperBound - lowerBound and index starts with lowerBound.
It's an asynchronous function, and thus, takes a completion block as last parameter. Do I repeat myself? Well, you should recognize the pattern! ;)
Here is the implementation:
void apply_each_with_range(int lowerBound, int upperBound,
unary_async_t task, completion_t completion)
{
do_apply(lowerBound, upperBound, task, completion);
}
And the other helper - which is finally performing some sort of "for_each index in range[upperBound, lowerBound] sequentially invoke task with parameter index":
static void do_apply(int index, int upperBound,
unary_async_t task, completion_t completion)
{
if (index >= upperBound) {
if (completion)
completion(@"OK");
return;
}
task(index, ^(id result) {
if (![result isKindOfClass:[NSError class]]) {
do_apply(index + 1, upperBound, task, completion);
}
else {
// error occurred: stop iterating and signal error
if (completion) {
completion(result);
}
}
});
}
The function do_apply
first checks whether the index is out of range. If, then it is finished and calls the completion handler with an @"OK".
Otherwise, it invokes task with argument index and provides a completion handler which gets invoked when task finished, which itself passes the result of the task. If that was successful, do_apply
will invoke itself with argument index incremented by one. This may look like a "recursion" - but it is not. do_apply
already returned, when it invokes itself.
If task returned and error, do_apply
stops, "returning" the error from task in its completion handler (which is finally provided by the call-site).
Now, you just need to put these pieces together in your project - which should be fairly easy.