AFSetworking background JSON parsing avoids block nesting

I have network code with strong JSON parsing. This must be done in the background so as not to block the main thread. The code is as follows:

-(void) getSomeDataWithParameters:(...)parameters completion:(void (^)(NSArray *data))completion
{
    NSURLRequest *req = ...
    AFJSONRequestOperation *op = [[AFJSONRequestOperation alloc] initWithRequest:req];

    // sometimes I have more requests

    // startOperations is a wrapper on AFHTTPClient enqueueBatchOfHTTPRequestOperations:progressBlock:completionBlock:
    // that handles errors and loading views
    [self startOperations:@[op] completionBlock:^(NSArray *operations) {

        // getBgQueue = return dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_async(getBgQueue(), ^{

            NSArray *data = [MyParserClass parseJSON:op.responseJSON inContext:self.localContext];

            [self.localContext MR_saveToPersistentStoreWithCompletion:^(BOOL success, NSError *error) {
                // this is executed on main thread
                if(completion) completion(...);
            }];

        });

    }];
}

(AFNetworking 1.x)

The code above works very well, but it is a pain to set up and write. And often, all the content of a method is wrapped inside another block to get some required data first ... basically the blocks just accumulate and make ugly code

I use enqueueBatchOfHTTPRequestOperations, rather than separate completion blocks on AFJSONRequestOperation, because a batch termination block is sometimes run before all individual completion blocks ... (I also read that Matt did not encourage this)

Any pointers on how to do better than this?

+4
1

, , , "longcat is long", : " - -". , , , -MR_saveToPersistentStoreWithCompletion, data, -getSomeDataWithParameters..., data -startOperations.

, , , , __block, , , . , .

... , op -startOperations. , op, -startOperations: @[op] ..., , , , op operations . , :

- (void)getSomeDataWithParameters:(...)parameters completion:(void (^)(NSArray *data))completion
{
    NSURLRequest *req = ...;
    AFJSONRequestOperation *op = [[AFJSONRequestOperation alloc] initWithRequest:req];

    [self startOperations:@[op] completionBlock:^(NSArray *operations) {
        for (AFJSONRequestOperation *op in operations) {
            dispatch_async(getBgQueue(), ^{
                NSArray *data = [MyParserClass parseJSON:op.responseJSON inContext:self.localContext];
                void (^mrSaveCompletion)(BOOL, NSError*) = completion ? ^(BOOL success, NSError *error) { completion(data); } : nil;
                [self.localContext MR_saveToPersistentStoreWithCompletion: mrSaveCompletion];
            });
        }
    }];
}

. , , for dispatch_async.

"" dispatch_async. , -startOperations:... , , , . , :

- (void)startOperations: (NSArray*)ops completionQueue: (dispatch_queue_t)queue completionBlock: (void (^)(NSArray*))completion
{
    void (^completionWrapper)(NSArray*) = !completion ? nil : ^(NSArray* ops) {
        if (queue)
            dispatch_async(queue, ^{ completion(ops); });
        else
            completion(ops);
    };

    [self startOperations: ops completionBlock: completionWrapper];
}

- (void)getSomeDataWithParameters:(...)parameters completion:(void (^)(NSArray *data))completion
{
    NSURLRequest *req = ...;
    AFJSONRequestOperation *op = [[AFJSONRequestOperation alloc] initWithRequest:req];
    [self startOperations:@[op] completionQueue: getBgQueue() completionBlock:^(NSArray *operations) {
        for (AFJSONRequestOperation *op in operations) {
            NSArray *data = [MyParserClass parseJSON:op.responseJSON inContext:self.localContext];
            void (^mrSaveCompletion)(BOOL, NSError*) = !completion ? nil : ^(BOOL success, NSError *error) { completion(data); };
            [self.localContext MR_saveToPersistentStoreWithCompletion: mrSaveCompletion];
        });
    }];
}
+1

All Articles