最近、App Store にアプリを提出しました。アプリ内購入を購入しようとするとクラッシュすることが顧客から報告されています。残念ながら、開発ビルドでこれを複製することはできません。クラッシュログは次のとおりです。
https://www.dropbox.com/s/ml9xvb1pk7hxtiu/memejump.crash
xcode 5 を使用しています。コンパイラに問題がある可能性はありますか?
編集:
はい、すべての Iap が送信されました。クラッシュが発生した理由はわかっていると思います。アラート ビューがポップアップしないように、リクエストが購入のためのものなのか、単に価格を取得するためのものなのかをチェックする、膨大で複雑で信頼性の低い一連の if ステートメントがあります。何か問題があるに違いないのですが、見つかりません... 誰かがそれが購入なのか、それとも表示価格を取得するための単なるリクエストなのかを確認する方法を知っていますか? クラッシュを象徴するコードをすぐに公開します。
#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