In-App Purchases in iOS 6 Tutorial: Consumables and Receipt Validation
This is a blog post by site administrator Ray Wenderlich, an independent software developer and gamer. A while back on the weekly tutorial poll on the sidebar, you guys voted for a tutorial on In-App Purchases: consumables and receipt validation. As always, your wish is my command! :] This tutorial picks up where we left […] By Ray Wenderlich.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
In-App Purchases in iOS 6 Tutorial: Consumables and Receipt Validation
15 mins
This is a blog post by site administrator Ray Wenderlich, an independent software developer and gamer.
A while back on the weekly tutorial poll on the sidebar, you guys voted for a tutorial on In-App Purchases: consumables and receipt validation. As always, your wish is my command! :]
This tutorial picks up where we left things off in our previous In-App Purchases tutorial. As a reminder, in that tutorial we developed an app called “In App Rage” where customers could purchase rage face comics as non-consumable In-App purchases.
In this tutorial, you will extend the app to add a new consumable In-App Purchase – after all, those are often way more lucrative than non-consumables, since customers can purchase them more than once! You will also add receipt validation to add some extra security into the app.
Note that this tutorial (and the previous) are focused on the “simplest and easiest” way to do things, not the most “robust and flexible” way of doing things. If you’re more interested in the latter, check out our book iOS 6 by Tutorials, where you’ll learn how to develop an extremely flexible server-based In-App Purchases system, with downloadable content hosted on Apple’s servers!
So without further ado, let’s dive into some more In-App Purchases info – you’ll see it’s pretty easy, and there’s no need to rage about it! :]
Getting Started
Start by downloading the starter project for this tutorial.
Note that the starter project is not the same as where we left things off in the last tutorial – I’ve added a few extra things for you:
- Added the ability to see a rage comic after purchasing it (which was left as an exercise at the end of last tutorial).
- Added the skeleton for the “random rage face” feature. Right now you can do this an unlimited amount of times for testing.
- Gratuitous icons and iPhone 5 screen size support! :]
You’ll have to make a few changes to this project for it to work for you:
- Open RageIAPHelper.m and MasterViewController.m and search for “com.razeware” – you’ll see the product identifiers I set up. Replace those with your own product identifiers you created last time in iTunes Connect.
- Open In App Rage-Info.plist and update your Bundle Identifier to your own bundle identifier.
Once you’re done, try out the app on your device and purchase a comic and make sure the purchase succeeds, and that you can view the comic after you buy it. Also, try out the new random rage face feature!
Creating a Consumable In-App Purchase
You have an sneaky plan to make money – instead of allowing the user to see cool random rage faces as often as they’d like, instead you’ll add an arbitrary limit to it, and charge users to purchase more, mwuhahaha!
This way, there’s no limit to how much money users can spend on your app, hopefully keeping the bucks rolling in!
Note: This is just the beginning of ways you can use consumable In-App Purchases to (arguably sneakily) increase your app’s revenue. To learn more, check out the many blogs and talks about freemium game design, gamer psychology, or any games by Zynga ;]
Note: This is just the beginning of ways you can use consumable In-App Purchases to (arguably sneakily) increase your app’s revenue. To learn more, check out the many blogs and talks about freemium game design, gamer psychology, or any games by Zynga ;]
Just like you did with consumable In-App Purchases, the first step to make this all happen is to create a new In-App Purchase entry in iTunes Connect.
So log onto iTunes Connect, and click Manage Your Applications. Click your entry for In-App Rage, click Manage In-App Purchases, and Create New. Underneath the Consumable option, click Select.
Next, fill out the screen that appears as follows (but replace razeware with your own name/company name):
Finally, scroll down to the Language section and click Add Language. Fill in the dialog that appears as follows:
Click Save, then Save again, and you’re done! Time to try this out in your app.
Implementing a Consumable In-App Purchase
To begin, open RageIAPHelper.m and add the new product identifier you jsut created to the bottom of the list of productIdentifiers, similar to the following:
NSSet * productIdentifiers = [NSSet setWithObjects:
@"com.razeware.inapprage.drummerrage",
@"com.razeware.inapprage.itunesconnectrage",
@"com.razeware.inapprage.nightlyrage",
@"com.razeware.inapprage.studylikeaboss",
@"com.razeware.inapprage.updogsadness",
@"com.razeware.inapprage.randomrageface",
nil];
Next, open IAPHelper.m and modify your provideContentForProductIdentifier: method as follows:
- (void)provideContentForProductIdentifier:(NSString *)productIdentifier {
if ([productIdentifier isEqualToString:@"com.razeware.inapprage.randomrageface"]) {
int currentValue = [[NSUserDefaults standardUserDefaults]
integerForKey:@"com.razeware.inapprage.randomrageface"];
currentValue += 5;
[[NSUserDefaults standardUserDefaults] setInteger:currentValue
forKey:@"com.razeware.inapprage.randomrageface"];
[[NSUserDefaults standardUserDefaults] synchronize];
} else {
[_purchasedProductIdentifiers addObject:productIdentifier];
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:productIdentifier];
[[NSUserDefaults standardUserDefaults] synchronize];
}
[[NSNotificationCenter defaultCenter]
postNotificationName:IAPHelperProductPurchasedNotification
object:productIdentifier userInfo:nil];
}
Here you add some special case behavior for the consumable In-App Purchase you added. When a customer purchases this, instead of simply setting a flag indicating whether it’s purchased or not, you increment a value that keeps track of how many rolls the user currently has.
Note: This works but isn’t the ideal way of doing things, because now your IAPHelper class has hard-coded logic for a particular In-App Purchase so isn’t as reusable. For a more reusable way of doing things, check out iOS 6 by Tutorials.
Note: This works but isn’t the ideal way of doing things, because now your IAPHelper class has hard-coded logic for a particular In-App Purchase so isn’t as reusable. For a more reusable way of doing things, check out iOS 6 by Tutorials.
Next open MasterViewController.m and replace your tableView:numberOfRowsInSection: and tableView:cellForRowAtIndexPath: methods as follows:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return _products.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"
forIndexPath:indexPath];
SKProduct * product = (SKProduct *) _products[indexPath.row];
cell.textLabel.text = product.localizedTitle;
[_priceFormatter setLocale:product.priceLocale];
cell.detailTextLabel.text = [_priceFormatter stringFromNumber:product.price];
if (![product.productIdentifier isEqualToString:@"com.razeware.inapprage.randomrageface"] &&
[[RageIAPHelper sharedInstance] productPurchased:product.productIdentifier]) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
cell.accessoryView = nil;
} else {
UIButton *buyButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
buyButton.frame = CGRectMake(0, 0, 72, 37);
[buyButton setTitle:@"Buy" forState:UIControlStateNormal];
buyButton.tag = indexPath.row;
[buyButton addTarget:self action:@selector(buyButtonTapped:)
forControlEvents:UIControlEventTouchUpInside];
cell.accessoryType = UITableViewCellAccessoryNone;
cell.accessoryView = buyButton;
}
return cell;
}
Here you have removed the “test” row that the sample project had earlier, since now you have a new consumable In-App Purchase to display instead. You also add some logic so that you always display the “Buy” button for the consumable IAP.
Finally, open RandomFaceViewController.m and replace the buttonTapped: method with this (and these additional two methods):
- (void)refresh {
int currentValue = [[NSUserDefaults standardUserDefaults]
integerForKey:@"com.razeware.inapprage.randomrageface"];
self.label.text = [NSString stringWithFormat:@"Times Remaining: %d", currentValue];
}
- (void)viewWillAppear:(BOOL)animated {
[self refresh];
}
- (IBAction)buttonTapped:(id)sender {
int currentValue = [[NSUserDefaults standardUserDefaults]
integerForKey:@"com.razeware.inapprage.randomrageface"];
if (currentValue <= 0) return;
currentValue--;
[[NSUserDefaults standardUserDefaults] setInteger:currentValue
forKey:@"com.razeware.inapprage.randomrageface"];
[self refresh];
int randomIdx = (arc4random() % 4) + 1;
NSString * randomName = [NSString stringWithFormat:@"random%d.png", randomIdx];
self.imageView.image = [UIImage imageNamed:randomName];
}
This is some basic logic to replace the unlimited button taps with using the new value you're using to track the user's spins.
And that's it! Build and run, and purchase a few spins. Then test out your the random rage face button and make sure it works as expected!