Note that there are some explanatory texts on larger screens.

plurals
  1. POSIGSEGV Crash from StoreKit - unable to replicate
    primarykey
    data
    text
    <p>A client has brought an existing app to me, and we've just released a new version including Crittercism for crash reports.</p> <p>Since release, we've been getting a load of crash reports as below. I believe that the crash is caused by the delegate of the SKProductRequest being released too soon, so I'm not looking for the answer to why the crash is happening - that's already been answered elsewhere on StackOverflow.</p> <p>My problem is that I can't replicate the bug. I've tried numerous devices, and different versions of iOS. From Crittercism, the crash is happening on mainly up to date devices, and a range of iPhone, iPod and iPad - so it is not one particular type of device, but I still can't make it happen. I've downloaded the Lite version, and from there, bought the Full version - it all works perfectly.</p> <p>My question therefore is does anyone have any idea how I can make it happen on my devices so that I can fix it?!</p> <pre><code>libobjc.A.dylib 0x37393f78 objc_msgSend + 15 StoreKit 0x37bc3a4f -[SKProductsRequest handleFinishResponse:returningError:] + 142 StoreKit 0x37bc4dc7 -[SKRequest _requestFinishedNotification:] + 210 Foundation 0x319624ff __57-[NSNotificationCenter addObserver:selector:name:object:]_block_invoke_0 + 18 CoreFoundation 0x31027547 ___CFXNotificationPost_block_invoke_0 + 70 CoreFoundation 0x30fb3097 _CFXNotificationPost + 1406 Foundation 0x318d63eb -[NSNotificationCenter postNotificationName:object:userInfo:] + 66 AppSupport 0x314eeba3 -[CPDistributedNotificationCenter deliverNotification:userInfo:] + 62 AppSupport 0x314f010b _CPDNDeliverNotification + 290 AppSupport 0x314ee99b _XDeliverNotification + 170 AppSupport 0x314e3b11 migHelperRecievePortCallout + 172 CoreFoundation 0x3102f523 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 38 CoreFoundation 0x3102f4c5 __CFRunLoopDoSource1 + 140 CoreFoundation 0x3102e313 __CFRunLoopRun + 1370 CoreFoundation 0x30fb14a5 CFRunLoopRunSpecific + 300 CoreFoundation 0x30fb136d CFRunLoopRunInMode + 104 GraphicsServices 0x3302b439 GSEventRunModal + 136 UIKit 0x30714cd5 UIApplicationMain + 1080 MyAppLite 0x000fc7c3 main (main.m:13) </code></pre> <p>The culprit is bound to be in here somewhere, but I still can't make it crash on my devices, or in the simulator:</p> <pre><code>#import "InAppPurchaseViewController.h" #define INDICATOR_Y 150.0f #define INDICATOR_MOVE_Y 300.0f #define PRODUCT_LABEL_Y 150.0f #define PURCHASE_BUTTON_Y 190.0f #define RESTORE_BUTTON_Y 240.0f @interface InAppPurchaseViewController (Private) - (void)updateUIToDefaultState; @end @implementation InAppPurchaseViewController - (void) dealloc { [productLabel release]; [purchaseButton release]; [indicatorView release]; [super dealloc]; } #pragma mark - #pragma mark UIViewController - (void)viewDidLoad { [super viewDidLoad]; self.title = @"..."; self.view.backgroundColor = [UIColor blackColor]; self.tableView.backgroundColor = [UIColor blackColor]; self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; self.tableView.scrollEnabled = NO; self.tableView.indicatorStyle = UIScrollViewIndicatorStyleWhite; UIImageView *headerImageView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:@"upgrade-header.png"]] autorelease]; UIView *headerView = [[[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, self.view.bounds.size.width, headerImageView.image.size.height + 200.0)] autorelease]; [headerView addSubview:headerImageView]; indicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]; indicatorView.hidesWhenStopped = YES; [indicatorView startAnimating]; [headerView addSubview:indicatorView]; productLabel = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 300.0f, 0.0f)]; productLabel.text = @"..."; productLabel.hidden = YES; productLabel.font = [UIFont systemFontOfSize:18.0f]; productLabel.textColor = [UIColor whiteColor]; productLabel.backgroundColor = [UIColor blackColor]; [productLabel sizeToFit]; [headerView addSubview:productLabel]; purchaseButton = [[UIButton buttonWithType:UIButtonTypeCustom] retain]; purchaseButton.titleLabel.font = [UIFont boldSystemFontOfSize:20.0f]; UIImage *bgImage = [UIImage imageNamed:@"btn_purchase.png"]; UIImage *buttonImage = [bgImage stretchableImageWithLeftCapWidth:(bgImage.size.width / 2.0f) - 1 topCapHeight:0.0f]; [purchaseButton setBackgroundImage:buttonImage forState:UIControlStateNormal]; [purchaseButton setTitle:@"Purchase" forState:UIControlStateNormal]; [purchaseButton addTarget:self action:@selector(purchaseClicked:) forControlEvents:UIControlEventTouchUpInside]; purchaseButton.frame = CGRectMake(0.0f, 0.0f, 300.0f, buttonImage.size.height); purchaseButton.hidden = YES; [self.view addSubview:purchaseButton]; restoreButton = [[UIButton buttonWithType:UIButtonTypeCustom] retain]; restoreButton.enabled = NO; restoreButton.titleLabel.font = [UIFont boldSystemFontOfSize:18.0f]; [restoreButton setBackgroundImage:buttonImage forState:UIControlStateNormal]; [restoreButton setTitle:@"Restore Purchases" forState:UIControlStateNormal]; [restoreButton addTarget:self action:@selector(restoreClicked:) forControlEvents:UIControlEventTouchUpInside]; restoreButton.frame = CGRectMake(0.0f, 0.0f, 300.0f, buttonImage.size.height); [self.view addSubview:restoreButton]; headerImageView.center = headerView.center; headerImageView.frame = CGRectMake(headerImageView.frame.origin.x, 0.0f, headerImageView.frame.size.width, headerImageView.frame.size.height); indicatorView.center = headerView.center; indicatorView.frame = CGRectMake(indicatorView.frame.origin.x, INDICATOR_Y, indicatorView.frame.size.width, indicatorView.frame.size.height); productLabel.center = headerView.center; productLabel.frame = CGRectMake(productLabel.frame.origin.x, PRODUCT_LABEL_Y, productLabel.frame.size.width, productLabel.frame.size.height); purchaseButton.center = headerView.center; purchaseButton.frame = CGRectMake(purchaseButton.frame.origin.x, PURCHASE_BUTTON_Y, purchaseButton.frame.size.width, purchaseButton.frame.size.height); restoreButton.center = headerView.center; restoreButton.frame = CGRectMake(restoreButton.frame.origin.x, RESTORE_BUTTON_Y, restoreButton.frame.size.width, restoreButton.frame.size.height); self.tableView.tableHeaderView = headerView; [self performSelectorInBackground:@selector(retrieveProductDetails:) withObject:nil]; } #pragma mark - #pragma mark AppStoreServiceDelegate - (void)productDetailsRequestSucceededWithResponse:(SKProductsResponse *)response { if (response.products.count == 1) { [self performSelectorOnMainThread:@selector(productDetailsRetrieved:) withObject:[response.products objectAtIndex:0] waitUntilDone:NO]; } else { NSString *message = @"Unable to retrieve product details: No valid product to purchase. Please contact support."; [self performSelectorOnMainThread:@selector(appStoreRequestFailed:) withObject:message waitUntilDone:NO]; } } - (void)productDetailsRequestFailedWithError:(NSError *)error { NSString *message = [NSString stringWithFormat:@"Unable to retrieve product details: %@", [error localizedDescription]]; [self performSelectorOnMainThread:@selector(appStoreRequestFailed:) withObject:message waitUntilDone:NO]; } - (void)transactionSucceededForProductId:(NSString *)productId { [self performSelectorOnMainThread:@selector(purchaseCompleted:) withObject:productId waitUntilDone:NO]; } - (void)transactionFailedWithReason:(NSString *)reason { NSString *message = [NSString stringWithFormat:@"Sorry, your purchase could not be completed: %@", reason]; [self performSelectorOnMainThread:@selector(appStoreRequestFailed:) withObject:message waitUntilDone:NO]; } - (void)transactionCancelled { [self performSelectorOnMainThread:@selector(updateUIToDefaultState) withObject:nil waitUntilDone:NO]; } #pragma mark - #pragma mark UIAlertViewDelegate (purchase failed) - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { [self.navigationController popViewControllerAnimated:YES]; } #pragma mark - #pragma mark Private button callbacks - (void)purchaseClicked:(UIButton *)clicked { clicked.enabled = NO; [indicatorView startAnimating]; indicatorView.frame = CGRectMake(indicatorView.frame.origin.x, INDICATOR_MOVE_Y, indicatorView.frame.size.width, indicatorView.frame.size.height); [self performSelectorInBackground:@selector(purchaseProduct:) withObject:nil]; } - (void)restoreClicked:(UIButton *)clicked { clicked.enabled = NO; [indicatorView startAnimating]; indicatorView.frame = CGRectMake(indicatorView.frame.origin.x, INDICATOR_MOVE_Y, indicatorView.frame.size.width, indicatorView.frame.size.height); [self performSelectorInBackground:@selector(restoreProducts:) withObject:nil]; } #pragma mark - #pragma mark Private - (void)showPurchaseDetailsWithName:(NSString *)productName price:(NSString *)price { self.title = productName; productLabel.text = [NSString stringWithFormat:@"%@, %@", productName, price]; [productLabel sizeToFit]; productLabel.center = self.tableView.tableHeaderView.center; [self updateUIToDefaultState]; } - (void)productDetailsRetrieved:(SKProduct *)productDetails { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; [indicatorView stopAnimating]; [self showPurchaseDetailsWithName:productDetails.localizedTitle price:[[AppStoreService sharedAppStoreService] formatPrice:productDetails]]; [pool drain]; } - (void)purchaseCompleted:(NSString *)productId { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; [indicatorView stopAnimating]; [[AppStoreService sharedAppStoreService] setPurchasedFullEdition:YES]; [self.navigationController popViewControllerAnimated:YES]; [pool drain]; } - (void)purchaseProduct:(id)ignored { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; [[AppStoreService sharedAppStoreService] purchaseProducts:[NSSet setWithObject:[[AppStoreService sharedAppStoreService] inAppProductIdentifierForEdition]] notifyingDelegate:self]; [pool drain]; } - (void)restoreProducts:(id)ignored { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; [[AppStoreService sharedAppStoreService] retoreCompletedTransactionsNotifyingDelegate:self]; [pool drain]; } - (void)appStoreRequestFailed:(NSString *)reason { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; [[[[UIAlertView alloc] initWithTitle:@"Purchase Error" message:reason delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil] autorelease] show]; [self updateUIToDefaultState]; [pool drain]; } - (void)retrieveProductDetails:(id)ignored { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; [[AppStoreService sharedAppStoreService] requestDetailsOfProducts:[NSSet setWithObject:[[AppStoreService sharedAppStoreService] inAppProductIdentifierForEdition]] notifyingDelegate:self]; [pool drain]; } - (void)updateUIToDefaultState { [indicatorView stopAnimating]; productLabel.hidden = NO; purchaseButton.hidden = NO; restoreButton.enabled = YES; purchaseButton.enabled = YES; } @end </code></pre> <p>Here's the AppStoreService.m</p> <pre><code>// AppStoreService.m #import "AppStoreService.h" #import "SynthesizeSingleton.h" #import "DataService.h" #import "ConfigService.h" #pragma mark - #pragma mark Private internal app store delegates @implementation AppStoreProductRequestDelegate @synthesize delegate = _delegate; - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response { //NSLog(@"Got response... %@", response); [request release]; if (self.delegate) { [self.delegate productDetailsRequestSucceededWithResponse:response]; } } - (void)request:(SKRequest *)request didFailWithError:(NSError *)error { //NSLog(@"Got error... %@", error); [request release]; if (self.delegate) { [self.delegate productDetailsRequestFailedWithError:error]; } } @end @implementation AppStoreTransactionObserver @synthesize delegate = _delegate; - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions { for (SKPaymentTransaction *transaction in transactions) { switch (transaction.transactionState) { case SKPaymentTransactionStatePurchased: { [self completeTransaction:transaction]; break; } case SKPaymentTransactionStateFailed: { [self failedTransaction:transaction]; break; } case SKPaymentTransactionStateRestored: {[self restoreTransaction:transaction]; break; } default: break; } } } - (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error { if (self.delegate) { [self.delegate transactionFailedWithReason:[NSString stringWithFormat:@"Purchase failed: %@", [error localizedDescription]]]; } } - (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue { //NSLog(@"paymentQueueRestoreCompletedTransactionsFinished:"); } - (void)failedTransaction:(SKPaymentTransaction *)transaction { //NSLog(@"failedTransaction: %@", transaction); if (self.delegate) { if (transaction.error.code == SKErrorPaymentCancelled) { [self.delegate transactionCancelled]; } else { [self.delegate transactionFailedWithReason:[NSString stringWithFormat:@"Purchase failed: %@", [transaction.error localizedDescription]]]; } } [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; } - (void)restoreTransaction:(SKPaymentTransaction *)transaction { //NSLog(@"restoreTransaction: %@", transaction); if (self.delegate) { [self.delegate transactionSucceededForProductId:transaction.originalTransaction.payment.productIdentifier]; } [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; } - (void)completeTransaction:(SKPaymentTransaction *)transaction { //NSLog(@"completeTransaction: %@", transaction); if (self.delegate) { [self.delegate transactionSucceededForProductId:transaction.payment.productIdentifier]; } [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; } @end #pragma mark - #pragma mark AppStoreService @implementation AppStoreService SYNTHESIZE_SINGLETON_FOR_CLASS(AppStoreService); static NSString *kLPHasPurchasedFullEdition = @"kLPHasPurchasedFullEdition"; - (AppStoreService *)init { if (self = [super init]) { productDetailsDelegate = [[AppStoreProductRequestDelegate alloc] init]; appStoreObserver = [[AppStoreTransactionObserver alloc] init]; [[SKPaymentQueue defaultQueue] addTransactionObserver:appStoreObserver]; } return self; } - (void) dealloc { [[SKPaymentQueue defaultQueue] removeTransactionObserver:appStoreObserver]; [productDetailsDelegate release]; [appStoreObserver release]; [super dealloc]; } - (BOOL)hasPurchasedFullEdition { return [[NSUserDefaults standardUserDefaults] boolForKey:kLPHasPurchasedFullEdition]; } - (void)setPurchasedFullEdition:(BOOL)purchased { //NSLog(@"Purchased? %d", purchased); [[NSUserDefaults standardUserDefaults] setBool:purchased forKey:kLPHasPurchasedFullEdition]; [[ConfigService sharedConfigService] synchronizeConfig]; } - (NSString *)inAppProductIdentifierForEdition { if ([[DataService sharedDataService] isLiteEdition]) { if ([DataService sharedDataService].isLanguageEdition) { // Note. Remove the "-" from language codes, e.g. Brazillian Portugese pt-br, as in-app purchase IDs cannot contain a hyphen. NSString *fixedCode = [[DataService sharedDataService].languageCode stringByReplacingOccurrencesOfString:@"-" withString:@""]; return [NSString stringWithFormat:@"com.myBrokenApp.%@.AllCategories", fixedCode]; } else { return @"com.myBrokenApp.AllCategories"; } } else { @throw [NSException exceptionWithName:@"InvalidOperation" reason:@"An in app purchase product ID was requested for a non-lite version" userInfo:[NSDictionary dictionary]]; } } - (NSString *)purchasedCategoryIdsWhereClause { // flirting &amp; essentials for lite builds return ([DataService sharedDataService].isLiteEdition &amp;&amp; ![self hasPurchasedFullEdition]) ? @"and c.category_id in (1,19)" : @" "; } - (NSString *)formatPrice:(SKProduct *)product { NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init]; [numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4]; [numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle]; [numberFormatter setLocale:product.priceLocale]; NSString *currency = [numberFormatter stringFromNumber:product.price]; [numberFormatter release]; return currency; } - (void)requestDetailsOfProducts:(NSSet *)products notifyingDelegate:(id&lt;AppStoreServiceDelegate&gt;)delegate { //NSLog(@"Retrieving details of products: %@", products); SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:products]; productDetailsDelegate.delegate = delegate; request.delegate = productDetailsDelegate; //NSLog(@"Starting request..."); [request start]; } - (void)purchaseProducts:(NSSet *)products notifyingDelegate:(id&lt;AppStoreServiceDelegate&gt;)delegate { //NSLog(@"Making in app purchase for products: %@", products); if ([SKPaymentQueue canMakePayments]) { appStoreObserver.delegate = delegate; for (NSString *productId in products) { [[SKPaymentQueue defaultQueue] addPayment:[SKPayment paymentWithProductIdentifier:productId]]; } } else { [delegate transactionFailedWithReason:@"You are not permitted to make purchases."]; } } - (void)retoreCompletedTransactionsNotifyingDelegate:(id&lt;AppStoreServiceDelegate&gt;)delegate { //NSLog(@"Restoring in-app purchases..."); if ([SKPaymentQueue canMakePayments]) { appStoreObserver.delegate = delegate; [[SKPaymentQueue defaultQueue] restoreCompletedTransactions]; } else { [delegate transactionFailedWithReason:@"You are not permitted to make purchases."]; } } @end </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload