An iOS 7 In-App Purchase Tutorial

PreviousTable of ContentsNext
Preparing an iOS 7 Application for In-App PurchasesConfiguring and Creating App Store Hosted Content for iOS 7 In-App Purchases


Learn SwiftUI and take your iOS Development to the Next Level
SwiftUI Essentials – iOS 16 Edition book is now available in Print ($39.99) and eBook ($29.99) editions. Learn more...

Buy Print Preview Book


This chapter assumes that the steps outlined in the previous chapter (Preparing an iOS 7 Application for In-App Purchases) have been followed and implemented carefully. This chapter will continue the development of the InAppDemo project to demonstrate the application side of in-app purchasing.

The Application User Interface

When completed, the application will consist of three views (Figure 86 1). The goal is for the Level 2 view to be inaccessible from the Level 1 view until the user has made an in-app purchase using the product purchase view:


The scenes for an iOS 7 In App  Purchase tutorial example

Figure 86-1


Designing the Storyboard

Load the InAppDemo project into Xcode, select the Main.storyboard file, select the In App Demo View Controller scene and choose the Editor -> Embed In -> Navigation Controller menu option to add a navigation controller to the storyboard.

Next, design the user interface for the Level 1 screen as illustrated in the far left view of Figure 86-1. Using the Assistant Editor with the InAppDemoViewController.h file selected, establish an Action connection from the “Buy Level 2 Access” button to an action method named purchaseItem. Also establish an Outlet connection for the “Enter Level 2” button named level2Button.

On completion of these steps, the InAppDemoViewController.h file should read as follows:

#import <UIKit/UIKit.h>
#import <StoreKit/StoreKit.h>

@interface InAppDemoViewController : UIViewController
- (IBAction)purchaseItem:(id)sender;
@property (strong, nonatomic) IBOutlet UIButton *level2Button;
@end 

Add another scene to the storyboard by dragging and dropping a View Controller object from the Object Library onto the canvas. Add a label to the view of the new scene and change the label’s text to ”Welcome to Level 2”.

Establish a segue from the “Enter Level 2” button to the Level 2 scene by Ctrl clicking and dragging from the button to the new scene. Release the line and select push from the resulting menu.

Finally, select the “Enter Level 2” button, display the Attributes Inspector and turn off the Enabled checkbox in the Control section. This will ensure that the button is disabled until the user has purchased access to level 2.

The completed storyboard should appear as shown in Figure 86-2.


An IOS 7 in app purchase example storyboard

Figure 86-2



Creating the Purchase View Controller

Select File -> New -> File… and create a new Objective-C class named PurchaseViewController subclassed from UIViewController with the With XIB for user interface option selected. Select the PurchaseViewController.xib file and design the layout to match that shown in Figure 86-3, using Label, Text View and Button objects.

The user interface layout for an iOS 7 in app purchase app

Figure 86-3


Select the button object and stretch it so that it is approximately half the overall width of the containing view. Also remove the sample Latin text from the text view object using the Attributes Inspector.

Using the Assistant Editor with the PurchaseViewController.h file selected, establish outlets for the label, text view and button named productTitle, productDescription and buyButton respectively.

Next, establish an Action connection from the Buy button to a method named buyProduct. Locate and select the PurchaseViewController.h file, verify the outlets and action are correctly implemented, then add additional properties and declarations that will be needed later in the chapter:

#import <UIKit/UIKit.h>
#import <StoreKit/StoreKit.h>

@interface PurchaseViewController : UIViewController
<SKPaymentTransactionObserver, SKProductsRequestDelegate>

@property (strong, nonatomic) SKProduct *product;
@property (strong, nonatomic) NSString *productID;
@property (strong, nonatomic) IBOutlet UILabel *productTitle;
@property (strong, nonatomic) IBOutlet UIButton *buyButton;
@property (strong, nonatomic) IBOutlet UITextView *productDescription;
- (IBAction)buyProduct:(id)sender;
- (void)getProductInfo:(UIViewController *)viewController;
@end

Since the class will use the Store Kit Framework, the <StoreKit/StoreKit.h> file has also been imported. The class is also going to act as the transaction observer and products request delegate so this fact is declared. Properties have been added to allow the class to keep track of the product ID and SKProduct object. Finally, a method named getProductInfo which will be implemented later in the chapter has been declared.

Completing the InAppDemoViewController Class

In order to complete the InAppDemoViewController class implementation, select the InAppDemoViewController.h file and modify it to import <StoreKit/StoreKit.h> and PurchaseViewController.h. Also, declare a method named enableLevel2 and add a property to store a reference to an instance of the PurchaseViewController class.

#import <UIKit/UIKit.h>
#import <StoreKit/StoreKit.h>
#import "PurchaseViewController.h"	

@interface InAppDemoViewController : UIViewController

- (IBAction)purchaseItem:(id)sender;
@property (strong, nonatomic) IBOutlet UIButton *level2Button;
@property (strong, nonatomic) PurchaseViewController *purchaseController;
-(void)enableLevel2;
@end

Open the InAppDemoViewController.m file and modify the viewDidLoad method to create an instance of the PurchaseViewController class and to designate it as the transaction observer for the payment queue:

- (void)viewDidLoad
{
    [super viewDidLoad];
     
    _purchaseController = [[PurchaseViewController alloc]init];

    [[SKPaymentQueue defaultQueue] 
         addTransactionObserver:_purchaseController];
}

When the “Buy Level 2 Access” button is selected, the purchaseItem method is configured to be called. Locate the stub of this method and modify it as follows where <YOUR PRODUCT ID GOES HERE> is replaced by the product ID you created in iTunes Connect in the previous chapter:

- (IBAction)purchaseItem:(id)sender {

    _purchaseController.productID = 
           @"<YOUR PRODUCT ID GOES HERE>";

    [self.navigationController 
          pushViewController:_purchaseController animated:YES];

    [_purchaseController getProductInfo: self];
}

Note that if the product ID specified does not match that defined for the in-app purchase item created using iTunes Connect in the previous chapter purchase attempts will fail. Finally, implement the enableLevel2 method which will be called to enable the Level 2 access button once the purchase is complete:

-(void)enableLevel2
{
    _level2Button.enabled = YES;
}

The InAppDemoViewController class is now complete.

Completing the PurchaseViewController Class

The first steps in completing the PurchaseViewController class are to locally import the InAppDemoViewController.h file and use the class extension mechanism in the implementation file to declare a property that is private to the PurchaseViewController.m file in which to store a reference to the In App Demo View Controller object. Select PurchaseViewController.m in the project navigator, therefore, and modify it as follows:

#import "PurchaseViewController.h"
#import "InAppDemoViewController.h"

@interface PurchaseViewController ()
@property (strong, nonatomic) InAppDemoViewController *homeViewController;
@end

@implementation PurchaseViewController
.
.
@end

Until the product information has been obtained, the buy button should be disabled so modify the viewDidLoad method accordingly:

- (void)viewDidLoad
{
    [super viewDidLoad];  
    _buyButton.enabled = NO;
}

Learn SwiftUI and take your iOS Development to the Next Level
SwiftUI Essentials – iOS 16 Edition book is now available in Print ($39.99) and eBook ($29.99) editions. Learn more...

Buy Print Preview Book

When the user decides to purchase Level 2 access, the getProductInfo method of the PurchaseViewController instance will be called by the InAppDemoViewController instance. Passed as an argument to this method will be a reference to the InAppDemoViewController instance which needs to be saved in the previously declared homeViewController property. It will then be the job of the getProductInfo method to contact the App Store and get product information for the specified ID. The code for this belongs in PurchaseViewController.m and reads as follows:

-(void)getProductInfo: (InAppDemoViewController *) viewController
{
    _homeViewController = viewController;

    if ([SKPaymentQueue canMakePayments])
    {
        SKProductsRequest *request = [[SKProductsRequest alloc]
                        initWithProductIdentifiers:
                        [NSSet setWithObject:self.productID]];
        request.delegate = self;

        [request start];
    }
    else
        _productDescription.text = 
               @"Please enable In App Purchase in Settings";
}

The request for product information will result in a call to the didReceiveResponse: delegate method which should be implemented as follows:

#pragma mark -
#pragma mark SKProductsRequestDelegate

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

    NSArray *products = response.products;

    if (products.count != 0)
    {
        _product = products[0];
        _buyButton.enabled = YES;
        _productTitle.text = _product.localizedTitle;
        _productDescription.text = _product.localizedDescription;
    } else {
        _productTitle.text = @"Product not found";
    }

    products = response.invalidProductIdentifiers;

    for (SKProduct *product in products)
    {
        NSLog(@"Product not found: %@", product);
    }
}

Note that the above code displays the product information to the user and enables the Buy button. This is configured to call the buyProduct method, the stub for which now needs to be completed:

- (IBAction)buyProduct:(id)sender {
    SKPayment *payment = [SKPayment paymentWithProduct:_product];
    [[SKPaymentQueue defaultQueue] addPayment:payment];
}

This code will initiate the purchasing process and cause calls to be made to the updatedTransactions method of the transaction observer object. Since the PurchaseViewController instance was declared as the transaction observer, this method needs to be implemented in PurchaseViewController.m:

#pragma mark -
#pragma mark SKPaymentTransactionObserver

-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
        for (SKPaymentTransaction *transaction in transactions)
    {
        switch (transaction.transactionState) {
            case SKPaymentTransactionStatePurchased:
                [self unlockFeature];
                [[SKPaymentQueue defaultQueue] 
                     finishTransaction:transaction];
                break;

            case SKPaymentTransactionStateFailed:
                NSLog(@"Transaction Failed");
                [[SKPaymentQueue defaultQueue] 
                     finishTransaction:transaction];
                break;

            default:
                break;
        }
    }
}

Regardless of the success or otherwise of the purchase, the code finishes the transaction. In the event of a successful purchase, however, the unlockFeature method will be called, and should now be implemented in PurchaseViewController.m as follows:

-(void)unlockFeature
{
    _buyButton.enabled = NO;
    [_buyButton setTitle:@"Purchased" 
         forState:UIControlStateDisabled];
    [_homeViewController enableLevel2];
}

This method simply disables the Buy button, changes the text on the button to “Purchased” and calls the enableLevel2 method of the InAppDemoViewController instance, the reference to which was passed as an argument to the getProductInfo method and stored in the private homeViewController property.

Testing the Application

Connect an iOS device to the development system (in-app purchasing cannot be tested in the iOS Simulator environment). In the Settings application on the device, choose the iTunes & App Store option, select your usual account and choose Sign Out from the popup dialog.

Run the application and note that the “Enter Level 2” button is initially disabled. Touch the “Purchase Level 2 Access” button and, after a short delay, note that the product information appears on the purchase screen. Select the Buy button and wait for the purchase confirmation dialog to appear (Figure 86 4). Note that the dialog includes text which reads “[Environment: Sandbox]” to indicate that the sandbox is being used and that this is not a real purchase.


Making a test iOS 7 In App Purchase

Figure 86-4


Confirm the purchase and, when prompted to do so, enter the test account details configured in the previous chapter. When the purchase is complete the Buy button will change to “Purchased” and Level 2 should now be accessible.

Troubleshooting

By just about any standard, in-app purchasing is a multistep process and, as with any multistep process, implementation of in-app purchases can be susceptible to errors.

In the event that the example application does not work there are few areas that are worthwhile checking:

  • Verify that the application bundle ID matches the one used to create the provisioning profile and the app entry in iTunes.
  • Make sure that the matching developer profile is being used to sign the application
  • Check that the product ID used in code matches the ID assigned to the in-app purchase item in iTunes Connect.
  • Verify that the item was configured as being available for purchase in iTunes Connect.
  • Make sure that Tax and Banking details are entered and correct in iTunes Connect.
  • Try deleting the application from the device and re-installing it.

Summary

This chapter has taken the steps to complete a demonstration of in-app purchasing from within an iOS 7 application and provided some guidance in terms of troubleshooting tips in the event that in-app purchasing does not work.


Learn SwiftUI and take your iOS Development to the Next Level
SwiftUI Essentials – iOS 16 Edition book is now available in Print ($39.99) and eBook ($29.99) editions. Learn more...

Buy Print Preview Book



PreviousTable of ContentsNext
Preparing an iOS 7 Application for In-App PurchasesConfiguring and Creating App Store Hosted Content for iOS 7 In-App Purchases