Domanda

I recently submitted an app to the App Store. Customers have reported a crash when trying to purchase a in-app purchase (or any for that matter); but unfortunately I am not able to replicate this in the development build. Here is the crash log:

 https://www.dropbox.com/s/ml9xvb1pk7hxtiu/memejump.crash

Using xcode 5. is it possible to be a problem with the compiler?

EDIT:

Yes all Iaps are submitted I think I know why the crash is here. I have a huge, complicated and unreliable series of if statements checking to see if the request was made for a purchase or just to get the price so that an alert view doesn't pop up. There must be something wrong but I can't find it... Does anyone know a way to check if it is a purchase or just a request to get the price for display? I'll be posing the code soon with symbolicated crash.

#import "StoreViewController.h"

int internetActive;

BOOL forButtonsAndLabels;
BOOL forPurchase;
BOOL fakePurchase;

@interface StoreViewController ()

@end

@implementation StoreViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    //for internet test
    // check for internet connection
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(checkNetworkStatus:) name:kReachabilityChangedNotification object:nil];

    internetReachable = [Reachability reachabilityForInternetConnection];
    [internetReachable startNotifier];

    // check if a pathway to a random host exists
    hostReachable = [Reachability reachabilityWithHostName: @"www.apple.com"];
    [hostReachable startNotifier];

}


- (void)setButtonsAndLabelsForExtraCoins {

    if (internetActive == YES) {

        forButtonsAndLabels = YES;

        SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObjects:@"com.ge0rges.Meme_Jump.4000C",@"com.ge0rges.Meme_Jump.9000C", @"com.ge0rges.Meme_Jump.24000C", nil]];

        request.delegate = self;
        [request start];

        [activityindicatorButton24000 startAnimating];
        [activityindicatorButton4000 startAnimating];
        [activityindicatorButton9000 startAnimating];

        activityindicatorButton24000.hidden = NO;
        activityindicatorButton4000.hidden = NO;
        activityindicatorButton9000.hidden = NO;

        CoinsButton4000.hidden = YES;
        CoinsButton9000.hidden = YES;
        CoinsButton24000.hidden = YES;

    } else {

        [CoinsButton4000 setTitle:NSLocalizedString(@"N/A", nil) forState:UIControlStateNormal];
        [CoinsButton9000 setTitle:NSLocalizedString(@"N/A", nil) forState:UIControlStateNormal];
        [CoinsButton24000 setTitle:NSLocalizedString(@"N/A", nil) forState:UIControlStateNormal];

        [CoinsLabel4000 setText:nil];
        [CoinsLabel9000 setText:nil];
        [CoinsLabel24000 setText:nil];

        CoinsButton4000.hidden = NO;
        CoinsButton9000.hidden = NO;
        CoinsButton24000.hidden = NO;

        [activityindicatorButton24000 stopAnimating];
        [activityindicatorButton4000 stopAnimating];
        [activityindicatorButton9000 stopAnimating];

        forButtonsAndLabels = YES;
        forPurchase = NO;
    }
}

#pragma mark - checking internet
- (void)checkNetworkStatus:(NSNotification *)notice
{
    // called after network status changes
    NetworkStatus internetStatus = [internetReachable currentReachabilityStatus];
    switch (internetStatus)
    {
        case NotReachable:
        {
            internetActive = NO;
            forPurchase = NO;
            forButtonsAndLabels = YES;

            [self setButtonsAndLabelsForExtraCoins];

            break;
        }
        case ReachableViaWiFi:
        {
            internetActive = YES;

            [self setButtonsAndLabelsForExtraCoins];

            break;
        }
        case ReachableViaWWAN:
        {
            internetActive = YES;

            [self setButtonsAndLabelsForExtraCoins];

            break;
        }
    }
}

#pragma mark - buying extra coins

- (IBAction)buy4000Coins {

    CoinsButton4000.hidden = YES;

    [activityindicatorButton4000 startAnimating];
    activityindicatorButton4000.hidden = NO;

    if (internetActive == NO) {

        CoinsButton4000.hidden = NO;
        [activityindicatorButton4000 stopAnimating];

        UIAlertView *nointernet = [[UIAlertView alloc]
                                   initWithTitle: NSLocalizedString(@"No internet connection", nil)
                                   message: NSLocalizedString(@"It seems you are not connected to the internet. in-app purchases require a internet connection. Please connect to the internet and try again.", nil)
                         delegate:nil
                         cancelButtonTitle:@"OK"
                         otherButtonTitles:nil, nil];


        [nointernet show];

    } else {

        identifier = @"com.ge0rges.Meme_Jump.4000C";
        [self checkForParentPermission];

    }
}

- (IBAction)buy9000Coins {

    CoinsButton9000.hidden = YES;

    [activityindicatorButton9000 startAnimating];
    activityindicatorButton9000.hidden = NO;

    if (internetActive == NO) {

        CoinsButton9000.hidden = NO;
        [activityindicatorButton9000 stopAnimating];

        UIAlertView *nointernet = [[UIAlertView alloc]
                                   initWithTitle: NSLocalizedString(@"No internet connection", nil)
                                   message: NSLocalizedString(@"It seems you are not connected to the internet. in-app purchases require a internet connection. Please connect to the internet and try again.", nil)
                                   delegate:nil
                                   cancelButtonTitle:@"OK"
                                   otherButtonTitles:nil, nil];


        [nointernet show];

    } else {

        identifier = @"com.ge0rges.Meme_Jump.9000C";
        [self checkForParentPermission];

    }
}

- (IBAction)buy24000Coins {

    CoinsButton24000.hidden = YES;

    [activityindicatorButton24000 startAnimating];
    activityindicatorButton24000.hidden = NO;

    if (internetActive == NO) {

        CoinsButton24000.hidden = NO;
        [activityindicatorButton24000 stopAnimating];

        UIAlertView *nointernet = [[UIAlertView alloc]
                                   initWithTitle: NSLocalizedString(@"No internet connection", nil)
                                   message: NSLocalizedString(@"It seems you are not connected to the internet. in-app purchases require a internet connection. Please connect to the internet and try again.", nil)
                                   delegate:nil
                                   cancelButtonTitle:@"OK"
                                   otherButtonTitles:nil, nil];


        [nointernet show];

    } else {

        identifier = @"com.ge0rges.Meme_Jump.24000C";
        [self checkForParentPermission];

    }
}


#pragma mark - Processing payment

-(void)checkForParentPermission {

    if ([SKPaymentQueue canMakePayments]) {
        if ([identifier isEqualToString:@"com.ge0rges.Meme_Jump.4000C"]) {

            SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObject:@"com.ge0rges.Meme_Jump.4000C"]];

            request.delegate = self;

            [request start];

            [activityindicatorButton4000 startAnimating];
            activityindicatorButton4000.hidden = NO;

        } else if ([identifier isEqualToString:@"com.ge0rges.Meme_Jump.9000C"]) {

            SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObject:@"com.ge0rges.Meme_Jump.9000C"]];

            request.delegate = self;

            [request start];

            [activityindicatorButton9000 startAnimating];
            activityindicatorButton9000.hidden = NO;

        } else if ([identifier isEqualToString:@"com.ge0rges.Meme_Jump.24000C"]) {

            SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObject:@"com.ge0rges.Meme_Jump.24000C"]];

            request.delegate = self;

            [request start];

            [activityindicatorButton24000 startAnimating];
            activityindicatorButton24000.hidden = NO;
        }

    } else {

        UIAlertView *tmp = [[UIAlertView alloc]
                            initWithTitle:NSLocalizedString(@"Prohibited", nil)
                            message:NSLocalizedString(@"Sorry , Parental Control has prohibited in-app purchases, you cannot make a purchase.", nil)
                            delegate:nil
                            cancelButtonTitle:@"Ok"
                            otherButtonTitles:nil, nil];
        [tmp show];

    }
}

-(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response

{

    if (forButtonsAndLabels == YES && internetActive == YES && forPurchase == NO) {

        [activityindicatorButton4000 stopAnimating];
        [activityindicatorButton9000 stopAnimating];
        [activityindicatorButton24000 stopAnimating];

        product1 = nil;
        product2 = nil;
        product3 = nil;

        int count = (int)[response.products count];

        if (count>0) {
            NSLog(@"%i", count);
            product1 = [response.products objectAtIndex:1];
            product2 = [response.products objectAtIndex:2];
            product3 = [response.products objectAtIndex:0];

            NSNumberFormatter *numberFormatter1 = [[NSNumberFormatter alloc] init];
            [numberFormatter1 setFormatterBehavior:NSNumberFormatterBehavior10_4];
            [numberFormatter1 setNumberStyle:NSNumberFormatterCurrencyStyle];
            [numberFormatter1 setLocale:product1.priceLocale];
            NSString *stringPrice1 = [numberFormatter1 stringFromNumber:product1.price];

            NSNumberFormatter *numberFormatter2 = [[NSNumberFormatter alloc] init];
            [numberFormatter2 setFormatterBehavior:NSNumberFormatterBehavior10_4];
            [numberFormatter2 setNumberStyle:NSNumberFormatterCurrencyStyle];
            [numberFormatter2 setLocale:product2.priceLocale];
            NSString *stringPrice2 = [numberFormatter2 stringFromNumber:product2.price];

            NSNumberFormatter *numberFormatter3 = [[NSNumberFormatter alloc] init];
            [numberFormatter3 setFormatterBehavior:NSNumberFormatterBehavior10_4];
            [numberFormatter3 setNumberStyle:NSNumberFormatterCurrencyStyle];
            [numberFormatter3 setLocale:product3.priceLocale];
            NSString *stringPrice3 = [numberFormatter3 stringFromNumber:product3.price];

            [CoinsButton4000 setTitle:stringPrice1 forState:UIControlStateNormal];
            [CoinsButton9000 setTitle:stringPrice2 forState:UIControlStateNormal];
            [CoinsButton24000 setTitle:stringPrice3 forState:UIControlStateNormal];

            CoinsButton4000.hidden = NO;
            CoinsButton9000.hidden = NO;
            CoinsButton24000.hidden = NO;

            CoinsLabel4000.adjustsFontSizeToFitWidth = NO;
            CoinsLabel9000.adjustsFontSizeToFitWidth = NO;
            CoinsLabel24000.adjustsFontSizeToFitWidth = NO;

            CoinsLabel4000.numberOfLines = 1;
            CoinsLabel9000.numberOfLines = 1;
            CoinsLabel24000.numberOfLines = 1;


            [CoinsLabel4000 setText:product1.localizedTitle];
            [CoinsLabel9000 setText:product2.localizedTitle];
            [CoinsLabel24000 setText:product3.localizedTitle];

            fakePurchase = YES;

        } else {

            [CoinsButton4000 setTitle:NSLocalizedString(@"N/A", nil) forState:UIControlStateNormal];
            [CoinsButton9000 setTitle:NSLocalizedString(@"N/A", nil) forState:UIControlStateNormal];
            [CoinsButton24000 setTitle:NSLocalizedString(@"N/A", nil) forState:UIControlStateNormal];

            [CoinsLabel4000 setText:nil];
            [CoinsLabel9000 setText:nil];
            [CoinsLabel24000 setText:nil];

            CoinsButton4000.hidden = NO;
            CoinsButton9000.hidden = NO;
            CoinsButton24000.hidden = NO;

            [activityindicatorButton24000 stopAnimating];
            [activityindicatorButton4000 stopAnimating];
            [activityindicatorButton9000 stopAnimating];

            forButtonsAndLabels = YES;
            forPurchase = NO;
            fakePurchase = YES;

        }

    } else if (forPurchase == YES && internetActive == YES && fakePurchase == NO) {

        validProduct = nil;

        int count = (int)[response.products count];

        if (count>0) {

            validProduct = [response.products objectAtIndex:0];

            SKPayment *payment = [SKPayment paymentWithProduct:validProduct];

            [[SKPaymentQueue defaultQueue] addTransactionObserver:self];

            [[SKPaymentQueue defaultQueue] addPayment:payment]; // <-- KA CHING!


        } else {

            UIAlertView *notAvail = [[UIAlertView alloc]
                                     initWithTitle:NSLocalizedString(@"Failed", nil)
                                     message:NSLocalizedString(@"Sorry there seems to be a problem. Please try again later.", nil)
                                     delegate:nil
                                     cancelButtonTitle:@"Ok"
                                     otherButtonTitles:nil, nil];

            [notAvail show];

        }

    } else {

        [CoinsButton4000 setTitle:NSLocalizedString(@"N/A", nil) forState:UIControlStateNormal];
        [CoinsButton9000 setTitle:NSLocalizedString(@"N/A", nil) forState:UIControlStateNormal];
        [CoinsButton24000 setTitle:NSLocalizedString(@"N/A", nil) forState:UIControlStateNormal];

        [CoinsLabel4000 setText:nil];
        [CoinsLabel9000 setText:nil];
        [CoinsLabel24000 setText:nil];

        CoinsButton4000.hidden = NO;
        CoinsButton9000.hidden = NO;
        CoinsButton24000.hidden = NO;

        [activityindicatorButton24000 stopAnimating];
        [activityindicatorButton4000 stopAnimating];
        [activityindicatorButton9000 stopAnimating];

        forButtonsAndLabels = YES;
        forPurchase = NO;
        fakePurchase = YES;
    }
}

-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
    for (SKPaymentTransaction *transaction in transactions) {
        switch (transaction.transactionState) {
            case SKPaymentTransactionStatePurchasing:
            {
                if ([identifier isEqualToString:@"com.ge0rges.Meme_Jump.4000C"]) {

                    [activityindicatorButton4000 startAnimating];
                    activityindicatorButton4000.hidden = NO;

                } else if ([identifier isEqualToString:@"com.ge0rges.Meme_Jump.9000C"]) {

                    [activityindicatorButton9000 startAnimating];
                    activityindicatorButton9000.hidden = NO;

                } else if ([identifier isEqualToString:@"com.ge0rges.Meme_Jump.24000C"]) {

                    [activityindicatorButton24000 startAnimating];
                    activityindicatorButton24000.hidden = NO;

                }

                break;
            }

            case SKPaymentTransactionStatePurchased:
            {
                [[SKPaymentQueue defaultQueue] finishTransaction:transaction];

                int receiptValue = (int)[self verifyReceipt:transaction];

                if (receiptValue == 0) {

                    if ([identifier isEqualToString:@"com.ge0rges.Meme_Jump.4000C"]) {

                        CoinsButton4000.hidden = NO;
                        [activityindicatorButton4000 stopAnimating];

                        UIAlertView *complete = [[UIAlertView alloc]
                                                 initWithTitle:NSLocalizedString(@"Complete", nil)
                                                 message:NSLocalizedString(@"You have received 4000 Coins.", nil)
                                                 delegate:nil
                                                 cancelButtonTitle:NSLocalizedString(@"Awesome!", nil)
                                                 otherButtonTitles:nil, nil];
                        [complete show];

                        allCoins += 4000;

                        [self setItUp];

                    } else if ([identifier isEqualToString:@"com.ge0rges.Meme_Jump.9000C"]) {

                        CoinsButton9000.hidden = NO;
                        [activityindicatorButton9000 stopAnimating];

                        UIAlertView *complete = [[UIAlertView alloc]
                                                 initWithTitle:NSLocalizedString(@"Complete", nil)
                                                 message:NSLocalizedString(@"You have received 9000 Coins.", nil)
                                                 delegate:nil
                                                 cancelButtonTitle:NSLocalizedString(@"Awesome!", nil)
                                                 otherButtonTitles:nil, nil];
                        [complete show];

                        allCoins += 9000;

                        [self setItUp];

                    } else if ([identifier isEqualToString:@"com.ge0rges.Meme_Jump.24000C"]) {

                        CoinsButton24000.hidden = NO;
                        [activityindicatorButton24000 stopAnimating];

                        UIAlertView *complete = [[UIAlertView alloc]
                                                 initWithTitle:NSLocalizedString(@"Complete", nil)
                                                 message:NSLocalizedString(@"You have received 24000 Coins.", nil)
                                                 delegate:nil
                                                 cancelButtonTitle:NSLocalizedString(@"Awesome!", nil)
                                                 otherButtonTitles:nil, nil];
                        [complete show];

                        allCoins += 24000;

                        [self setItUp];

                    }

                } else {


                    UIAlertView *failed = [[UIAlertView alloc]
                                           initWithTitle:NSLocalizedString(@"Receipt Invalid", nil)
                                           message:NSLocalizedString(@"The receipt could not be verified. If you are jailbroken please check for tweaks that may interfere with connection to iTunes or provide free in-app purchases 'like IAP-Cracker', otherwise check your internet connection. And don't worry you have not been charged", nil)
                                           delegate:nil
                                           cancelButtonTitle:@"Ok"
                                           otherButtonTitles:nil, nil];
                    [failed show];


                    if ([identifier isEqualToString:@"com.ge0rges.Meme_Jump.4000C"]) {

                        CoinsButton4000.hidden = NO;
                        [activityindicatorButton4000 stopAnimating];

                    } else if ([identifier isEqualToString:@"com.ge0rges.Meme_Jump.9000C"]) {

                        CoinsButton9000.hidden = NO;
                        [activityindicatorButton9000 stopAnimating];

                    } else if ([identifier isEqualToString:@"com.ge0rges.Meme_Jump.24000C"]) {

                        CoinsButton24000.hidden = NO;
                        [activityindicatorButton24000 stopAnimating];

                    }

                }

                break;
            }

            case SKPaymentTransactionStateRestored:
            {

                [[SKPaymentQueue defaultQueue] finishTransaction:transaction];

                if ([identifier isEqualToString:@"com.ge0rges.Meme_Jump.4000C"]) {

                    CoinsButton4000.hidden = NO;
                    [activityindicatorButton4000 stopAnimating];

                } else if ([identifier isEqualToString:@"com.ge0rges.Meme_Jump.9000C"]) {

                    CoinsButton9000.hidden = NO;
                    [activityindicatorButton9000 stopAnimating];

                } else if ([identifier isEqualToString:@"com.ge0rges.Meme_Jump.24000C"]) {

                    CoinsButton24000.hidden = NO;
                    [activityindicatorButton24000 stopAnimating];

                }

                break;
            }

            case SKPaymentTransactionStateFailed:
            {
                if (transaction.error.code != SKErrorPaymentCancelled && forPurchase == YES && fakePurchase == NO) {

                    UIAlertView *failed = [[UIAlertView alloc]
                                           initWithTitle:NSLocalizedString(@"Failed", nil)
                                           message:transaction.error.localizedDescription
                                           delegate:nil
                                           cancelButtonTitle:@"Ok"
                                           otherButtonTitles:nil, nil];
                    [failed show];


                } 

                [[SKPaymentQueue defaultQueue] finishTransaction:transaction];

                if ([identifier isEqualToString:@"com.ge0rges.Meme_Jump.4000C"]) {

                    CoinsButton4000.hidden = NO;
                    [activityindicatorButton4000 stopAnimating];

                } else if ([identifier isEqualToString:@"com.ge0rges.Meme_Jump.9000C"]) {

                    CoinsButton9000.hidden = NO;
                    [activityindicatorButton9000 stopAnimating];

                } else if ([identifier isEqualToString:@"com.ge0rges.Meme_Jump.24000C"]) {

                    CoinsButton24000.hidden = NO;
                    [activityindicatorButton24000 stopAnimating];

                }

                fakePurchase = NO;

                break;
            }
        }
    }
}

#pragma mark - verifying purchases

- (NSInteger)verifyReceipt:(SKPaymentTransaction *)transaction {

    NSString *jsonObjectString = [self encode:(uint8_t *)transaction.transactionReceipt.bytes length:transaction.transactionReceipt.length];
    NSString *completeString = [NSString stringWithFormat:@"http://ge0rges.com/verifyiap/verifyiap.php?receipt=%@", jsonObjectString];

    NSURL *urlForValidation = [NSURL URLWithString:completeString];

    NSMutableURLRequest *validationRequest = [[NSMutableURLRequest alloc] initWithURL:urlForValidation];
    [validationRequest setHTTPMethod:@"GET"];

    NSData *responseData = [NSURLConnection sendSynchronousRequest:validationRequest returningResponse:nil error:nil];

    NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];

    NSInteger response = [responseString integerValue];

    return response;
}

- (NSString *)encode:(const uint8_t *)input length:(NSInteger)length {

    static char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";

    NSMutableData *data = [NSMutableData dataWithLength:((length + 2) / 3) * 4];
    uint8_t *output = (uint8_t *)data.mutableBytes;

    for (NSInteger i = 0; i < length; i += 3) {
        NSInteger value = 0;
        for (NSInteger j = i; j < (i + 3); j++) {
            value <<= 8;

            if (j < length) {
                value |= (0xFF & input[j]);
            }
        }

        NSInteger index = (i / 3) * 4;
        output[index + 0] =                    table[(value >> 18) & 0x3F];
        output[index + 1] =                    table[(value >> 12) & 0x3F];
        output[index + 2] = (i + 1) < length ? table[(value >> 6)  & 0x3F] : '=';
        output[index + 3] = (i + 2) < length ? table[(value >> 0)  & 0x3F] : '=';
    }

    return [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void)viewDidUnload {
    [super viewDidUnload];

    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

@end
È stato utile?

Soluzione

Based on your crash, it looks like your SKProductsRequest is returning fewer items than you expect and you're accessing past the end of the returned array. You should confirm the size of that array before accessing things in it.

As for reasons why that array might be smaller than expected, you should look in iTunes connect for any issues with those IAPs or Google around for IAP debugging tips.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top