Ios – Method BOOL return from inside block

cocoa-touchiosobjective cobjective-c-blocksxcode

I'm trying to add Beeblex's new In App Purchase verification to my app, however i'm struggling passing a return value from within a block.

Here's the code I have now, and as you can see I set a BOOL value, then within the verification block I set the BOOL and return it at the end. However the return at the end is called before the block finishes, so what I need is to return the BOOL from within the block?

- (BOOL)verifyTransaction:(SKPaymentTransaction *)transaction
{
    if (![BBXIAPTransaction canValidateTransactions]) {
        return YES; // There is no connectivity to reach the server
    }

    BOOL __block toReturn = YES;
    BBXIAPTransaction *bbxTransaction = [[BBXIAPTransaction alloc] initWithTransaction:transaction];
    bbxTransaction.useSandbox = YES;
    [bbxTransaction validateWithCompletionBlock:^(NSError *error) {
        if (bbxTransaction.transactionVerified) {
            if (bbxTransaction.transactionIsDuplicate) {
                // The transaction is valid, but duplicate - it has already been sent to Beeblex in the past.
                NSLog(@"Transaction is a duplicate!");
                [FlurryAnalytics logEvent:@"Transaction duplicate!"];
                toReturn = NO;
            } else {
                // The transaction has been successfully validated and is unique
                NSLog(@"Transaction valid data:%@",bbxTransaction.validatedTransactionData);
                [FlurryAnalytics logEvent:@"Transaction verified"];
                toReturn = YES;
            }
        } else {
            // Check whether this is a validation error, or if something went wrong with Beeblex
            if (bbxTransaction.hasConfigurationError || bbxTransaction.hasServerError || bbxTransaction.hasClientError) {
                // The error was not caused by a problem with the data, but is most likely due to some transient networking issues
                NSLog(@"Transaction error caused by network, not data");
                [FlurryAnalytics logEvent:@"Transaction network error"];
                toReturn = YES;
            } else {
                // The transaction supplied to the validation service was not valid according to Apple
                NSLog(@"Transaction not valid according to Apple");
                [FlurryAnalytics logEvent:@"Transaction invalid!!"];
                toReturn = NO;
            }
        }
    }];

    NSLog(@"toReturn: %@",toReturn ? @"Yes" : @"No");
    return toReturn;
}

If I simply put return = NO; inside the block, I get compiler warnings of Incompatible block pointer types, and control may reach end of non-void block.

Best Answer

Beeblex developer here. The code inside -validateWithCompletionBlock: execute asynchronously (in the background, it needs to talk to our servers, so there's no point blocking your app completely while we wait for the Internet to do its thing).

Therefore, you need to rethink your approach to validating your receipts. Right now you, general workflow is:

  1. Complete purchase
  2. Call Beeblex
  3. Wait for response
  4. Check boolean value

But #3 returns right away, so this will never work. You should consider doing something like this:

  1. Complete purchase
  2. Show a “Please wait…” view, or something similar that advises the user that you're unlocking whatever they've purchased.
  3. Call Beeblex
  4. Inside the block, determine whether the validation succeeded or not, and then act to unlock the content from there.
  5. Sit idle until called by the block

Here's a quick-and-dirty example. I didn't compile it, so it probably has a few bugs, but it should give you the idea behind the intended usage pattern.

- (void) showWaitView {
    // Display a wait view
}


- (void) hideWaitView {
    // Hide the wait view
}


- (void) completeValidationWithValidateReceiptData:(NSDictionary *) receipt isRecoverableError(BOOL) isRecoverableError {
    [self hideWaitView];

    if (receipt) {
        // Unlock the content, tell the user
    } else {
        if (isRecoverableError) {
            // Probably a network error of some kind. Tell user they need to be connected,
            // and ask them to do it again.
        } else {
            // Keep the content locked, tell the user something went wrong
        }
    }
}

- (void) validateReceipt:(SKPaymentTransaction *) transaction {
    if (![BBXIAPTransaction canValidateTransactions]) {
        [self completeValidationWithValidateReceiptData:Nil isRecoverableError:YES];
        return;
    }

    BBXIAPTransaction *bbxTransaction = [[BBXIAPTransaction alloc] initWithTransaction:transaction];
    bbxTransaction.useSandbox = YES;

    [bbxTransaction validateWithCompletionBlock:^(NSError *error) {
        if (bbxTransaction.transactionVerified) {
            if (bbxTransaction.transactionIsDuplicate) {
                // The transaction is valid, but duplicate - it has already been sent to Beeblex in the past.
                [FlurryAnalytics logEvent:@"Transaction duplicate!"];
                [self completeValidationWithValidateReceiptData:Nil isRecoverableError:NO];
            } else {
                // The transaction has been successfully validated and is unique
                [FlurryAnalytics logEvent:@"Transaction verified"];
                [self completeValidationWithValidateReceiptData:bbxTransaction.validatedTransactionData isRecoverableError:NO];
            }
        } else {
            // Check whether this is a validation error, or if something went wrong with Beeblex
            if (bbxTransaction.hasConfigurationError || bbxTransaction.hasServerError || bbxTransaction.hasClientError) {
                // The error was not caused by a problem with the data, but is most likely due to some transient networking issues
                [FlurryAnalytics logEvent:@"Transaction network error"];
                [self completeValidationWithValidateReceiptData:Nil isRecoverableError:YES];
            } else {
                // The transaction supplied to the validation service was not valid according to Apple
                [FlurryAnalytics logEvent:@"Transaction invalid!!"];
                [self completeValidationWithValidateReceiptData:Nil isRecoverableError:NO];
            }
        }
    }];
}
Related Topic