Awesome Cowbell 2 and in-app Purchase

My friend Carmine and I made an app a few years ago called Awesome Cowbell. We didn’t update anything for a few years and I decided to add in-app purchase to it instead of having a free and paid version on the App Store.

The app wasn’t updated for a while so there was a bunch of iOS 9 related things I had to fix. I created a new project (required iOS 9), fixed the bugs, added in-app purchase, and used storyboards this time instead of the xib I used years ago. It was ready to submit.

Only 4 days after I submitted the app was In Review. After a few hours in review it got rejected because I only implemented a way to use in-app purchase to Buy, not to Restore. I had the code for restoring done already but I didn’t want to add another button to the app just for this. The rejection came on a Thursday so I figured I would fix it on Saturday morning before the Islanders game at 3 (LETS GO ISLANDERS!!)

When I sat down to add the restore function I still didn’t want to add a button for restoring. I decided to use a UIAlertController for this. I just wanted three options (Buy, Restore, Cancel), that’s all I think I need to do to get the app approved. Here is the code I used to do this:

- (void)tapsBuy{
NSLog(@"User requests to Unlock");
UIAlertController * alert = [UIAlertController alertControllerWithTitle:@"Purchase" message:@"Do you want to Restore a previous purchase or Buy it for the first time?" preferredStyle:UIAlertControllerStyleAlert];

UIAlertAction *restoreAction = [UIAlertAction
actionWithTitle:NSLocalizedString(@"Restore", @"Restore Action")
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action)
{
[self restore];
}];

UIAlertAction *okAction = [UIAlertAction
actionWithTitle:NSLocalizedString(@"Buy", @"Buy action")
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action)
{
[self purchase:validProduct];
}];
UIAlertAction *cancelAction = [UIAlertAction
actionWithTitle:NSLocalizedString(@"Cancel", @"Cancel Action")
style:UIAlertActionStyleCancel
handler:^(UIAlertAction *action)
{
}];

[alert addAction:cancelAction];
[alert addAction:okAction];
[alert addAction:restoreAction];
[self presentViewController:alert animated:YES completion:nil];
}

That function is linked to a UIButton that starts the whole buying process. Here is the code I use for in-app purchase and restoring:

- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{
validProduct = nil;
NSUInteger count = [response.products count];
if(count > 0){
validProduct = [response.products objectAtIndex:0];
NSLog(@"Products Available!");
}
else if(!validProduct){
NSLog(@"No products available");
//this is called if your product id is not valid, this shouldn't be called unless that happens.
}
}

- (IBAction)purchase:(SKProduct *)product{
SKPayment *payment = [SKPayment paymentWithProduct:product];
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}

- (IBAction) restore{
//this is called when the user restores purchases, you should hook this up to a button
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
isUnlocked = YES;
[[NSUserDefaults standardUserDefaults] setBool:isUnlocked forKey:@"isUnlocked"];
[[NSUserDefaults standardUserDefaults] synchronize];
}

- (void) paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
{
NSLog(@"received restored transactions: %lu", (unsigned long)queue.transactions.count);
for (SKPaymentTransaction *transaction in queue.transactions)
{
if(SKPaymentTransactionStateRestored){
NSLog(@"Transaction state -> Restored");
//called when the user successfully restores a purchase
[self doUnlockAll];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
}

}

}

- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions{
for(SKPaymentTransaction *transaction in transactions){
switch (transaction.transactionState){
case SKPaymentTransactionStatePurchasing: NSLog(@"Transaction state -> Purchasing");
//called when the user is in the process of purchasing, do not add any of your own code here.
break;
case SKPaymentTransactionStatePurchased:
//this is called when the user has successfully purchased the package (Cha-Ching!)
[self doUnlockAll]; //you can add your code for what you want to happen when the user buys the purchase here, for this tutorial we use removing ads
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
NSLog(@"Transaction state -> Purchased");
break;
case SKPaymentTransactionStateRestored:
NSLog(@"Transaction state -> Restored");
//add the same code as you did from SKPaymentTransactionStatePurchased here
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
[_buyButton setTitle:@"Unlocked" forState:UIControlStateNormal];
_buyButton.enabled = NO;
break;
case SKPaymentTransactionStateDeferred:
break;
case SKPaymentTransactionStateFailed:
//called when the transaction does not finnish
if(transaction.error.code != SKErrorPaymentCancelled){
NSLog(@"Transaction state -> Cancelled");
//the user cancelled the payment ;(
}
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
}
}
}

If you are still reading this at this point you might have noticed that I call a function doUnlockAll after the user buys or restores. Here is that code:

- (void)doUnlockAll{
isUnlocked = YES;
[[NSUserDefaults standardUserDefaults] setBool:isUnlocked forKey:@"isUnlocked"];
[[NSUserDefaults standardUserDefaults] synchronize];
_buyButton.enabled = NO;
_buyButton.hidden = YES;
volLabel.hidden = NO;
panLabel.hidden = NO;
pitchLabel.hidden = NO;
volSlider.hidden = NO;
panSlider.hidden = NO;
pitchSlider.hidden = NO;
self.admobBannerView.hidden = YES;
}

This is where everything is done when the purchase was made. First I set a BOOL to YES and store it in NSUserDefault (which I call in the viewDidLoad). Then I disabled the buy button and hid it and made the other buttons and sliders visible which are included when you buy the app. I also use Google Ad Mob (I use to use iAds but thats going away in June 2016) so I disabled that after the in-app purchase is made as well.

I have no idea if this is the best way to implement the buy/restore in-app purchases but it’s the way I chose to do it. Get Awesome Cowbell, it’s FREE to download! Follow me on Twitter @jon01 and let me know what you think.