An iPhone iOS 6 In-App Purchase Tutorial

From Techotopia
Revision as of 14:51, 3 October 2012 by Neil (Talk | contribs) (New page: <table border="0" cellspacing="0" width="100%"> <tr> <td width="20%">Previous<td align="center">[[iPhone iOS 6 Development Essential...)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search
PreviousTable of ContentsNext
Preparing an iOS 6 Application for In-App PurchasesConfiguring and Creating App Store Hosted Content for iOS 6 In-App Purchases


<google>BUY_IOS6</google>


This chapter assumes that the steps outlined in the previous chapter (Preparing an iOS 6 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.


Contents


The Application User Interface

When completed, the application will consist of three views (Figure 77 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:


[[Image:]]

Figure 77-1

Designing the Storyboard

Load the InAppDemo project into Xcode, select the MainStoryboard.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 77 1. Using the Assistant Editor, 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 Attribute 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 77-2.


[[Image:]]

Figure 77-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 77-3, using Label, Text View and Button objects.


[[Image:]]

Figure 77-3


Using the Assistant Editor, 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:

- (IBAction)purchaseItem:(id)sender {

    _purchaseController.productID = 
           @"com.ebookfrenzy.nonconsumable1";

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

    [_purchaseController getProductInfo: self];
}

Note that the product ID must match that defined for the in-app purchase item created using iTunes Connect in the previous chapter.

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;
}

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.

Adding the StoreKit Framework to the Build

Within the project navigator panel, select the InAppDemo target at the top of the list and in the main panel select the Build Phases tab. In the Link Binary with Libraries category, click on the + button and in the resulting list of libraries search for and add the StoreKit.framework library.

Testing the Application

Connect an iPhone 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 77 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.


[[Image:]]

Figure 77-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 iPhone iOS 6 application and provided some guidance in terms of troubleshooting tips in the event that in-app purchasing does not work.


<google>BUY_IOS6</google>



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