Vijay
Vijay

Reputation: 420

How can we handle it when a block in GCD fails to execute?

I am very new to the GCD approach. I am trying to sync data from the Internet using blocks in GCD. I have a situation where if the sync operation is in progress, and the network fails then my application crashes. When not using blocks, I am able to handle that by using the @try/@catch method. But when I am trying to do it with blocks and GCD, I am not able to handle the crash or the exception. Any ideas on how to handle the crash?

Calling sync accounts in the block:

dispatch_async(exampleQueue, ^{
    dispatch_async(dispatch_get_main_queue(), ^{
        [self sendSyncStatusUpdate:@"Loading Accounts"];
    });

    [AccountService syncAccounts];
    [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:YES] forKey:@"accountsFirstSyncCompleted"];

    dispatch_async(dispatch_get_main_queue(), ^{
        [self.tableView reloadData];
    });
});

-[AccountService syncAccounts] method:

@try {
    ZKDescribeSObject *accountDescription = [app.client describeSObject:@"Account"];
    NSString *query = [NSString stringWithFormat:@"Select %@ From Account Where LastModifiedDate > %@", [accountDescription fieldsAsCsv], [dateTimeFormatter stringFromDate:lastSyncDate]];

    ZKQueryResult *result = [[app.client query:query batchSize:[NSNumber numberWithInt:200]] retain];
}
@catch (ZKSoapException *e) {
    [RootViewController hadSyncError:e];
}

I am not able to catch the exception that occurs when we go offline while syncing.

Upvotes: 3

Views: 1976

Answers (1)

ipmcc
ipmcc

Reputation: 29886

This is totally doable. As a commenter suggests you can use @try/@catch in blocks without incident. Using your code, for example:

dispatch_async(exampleQueue, ^{
    dispatch_async(dispatch_get_main_queue(), ^{
        [self sendSyncStatusUpdate:@"Loading Accounts"];
    });

    BOOL error = NO;    

    @try {
        // I pasted this code in line, but you could take the @try/@catch out of the
        // syncAccounts method, and just let the exception bubble up to here.
        ZKDescribeSObject *accountDescription = [app.client describeSObject:@"Account"];
        NSString *query = [NSString stringWithFormat:@"Select %@ From Account Where LastModifiedDate > %@", [accountDescription fieldsAsCsv], [dateTimeFormatter stringFromDate:lastSyncDate]];

        ZKQueryResult *result = [[app.client query:query batchSize:[NSNumber numberWithInt:200]] retain];
    }
    @catch (ZKSoapException *e) 
    {
        dispatch_async(dispatch_get_main_queue(), ^{
            [RootViewController hadSyncError:e];
        });

        error = YES;
    }

    if (error)
    {
        dispatch_async(dispatch_get_main_queue(), ^{
            [self sendSyncStatusUpdate:@"Failed to Sync Accounts!"];
        });
        return;
    }

    [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:YES] forKey:@"accountsFirstSyncCompleted"];

    dispatch_async(dispatch_get_main_queue(), ^{
        [self.tableView reloadData];
    });
});

This is a perfectly fine way to use GCD.

Upvotes: 2

Related Questions