An iOS 10 In-App Purchase Tutorial

From Techotopia
Revision as of 04:48, 10 November 2016 by Neil (Talk | contribs) (Creating the Purchase View Controller Class)

Jump to: navigation, search

PreviousTable of ContentsNext
Preparing an iOS 10 Application for In-App PurchasesConfiguring and Creating App Store Hosted Content for iOS 10 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 10 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 109-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:


Ios 8 in app purchase scenes.png

Figure 109-1


Designing the Storyboard

Load the InAppDemo project into Xcode, select the Main.storyboard file, select the 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 109 1. Using the Assistant Editor with the ViewController.swift file selected, establish an Outlet connection for the “Enter Level 2” button named level2Button.

Select all the views in the storyboard and use the Auto Layout Align menu to configure Horizontally in Container constraints for the three objects. Select the uppermost label and use the Add New Constraints menu to establish a Spacing to nearest neighbor constraint in the top edge of the view. Repeat this step for the two buttons.

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”. With the label selected, use the Auto Layout Align menu to enable horizontal and vertical container constraints for the view.

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 show from the resulting menu.

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. Finally, drag and drop a third View Controller onto the storyboard canvas to represent the purchasing screen. Ctrl-click on the Buy Level 2 Access button and drag to the newly added view controller. On releasing the line, select show from the resulting menu.

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

The storyboard should appear as shown in Figure 109-2.


Ios 10 inapp purchase storyboard.png

Figure 109-2



Creating the Purchase View Controller Class

Select File -> New -> File… and create a new iOS Cocoa Touch Class named PurchaseViewController subclassed from UIViewController with the Also create XIB file option deselected.

Within the Main.storyboard file, select the blank view controller scene, display the Identity Inspector and change the class of the View Controller to the newly created PurchaseViewController class. With the PurchaseViewController scene selected in the storyboard canvas, add a Label, Text View and Button to the layout and configure the user interface design so that it resembles that of Figure 109 3:


Ios 10 in app purchase buy scene.png

Figure 109-3


Ctrl-click on each of the three views so that all are selected and use the Auto Layout Align menu to add Horizontally in Container constraints to all of the views. With the three views still selected, use the Add New Constraints menu to add a Spacing to nearest neighbor constraint on the top edges using the current value.

Select the Text View, display the Auto Layout Add New Constraints menu and set both Height and Width constraints using the current values.

Finally, remove the sample Latin text from the Text View object using the Attributes Inspector. Using the Assistant Editor with the PurchaseViewController.swift 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.

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

Remaining within the PurchaseViewController.swift file, verify the outlets and action are correctly implemented, then add additional properties and declarations that will be needed later in the chapter (<YOUR PRODUCT ID GOES HERE> is replaced by the identifier for the In App Purchase product created using the iTunes Connect portal in the previous chapter):

import UIKit
import StoreKit

class PurchaseViewController: UIViewController, SKPaymentTransactionObserver, SKProductsRequestDelegate {

    @IBOutlet weak var productTitle: UILabel!
    @IBOutlet weak var productDescription: UITextView!
    @IBOutlet weak var buyButton: UIButton!

    var product: SKProduct?
    var productID = "<YOUR PRODUCT ID GOES HERE>"
.
.
.

Note that purchase attempts will fail if the product ID specified does not match that defined for the in-app purchase item created using iTunes Connect.

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.

Storing the Home View Controller in the App Delegate Class

The PurchaseViewController class is responsible for handling the purchase of access to the level 2 scene within the application. As such, it will be the responsibility of the PurchaseViewController to enable the Enter Level 2 button in the first view controller when a purchase has been completed. This will be achieved by making a call to a method in the first view controller. In order to be able to access this method, a reference to the first view controller needs to be accessible to the PurchaseViewController instance. For the purposes of this example, this will be achieved by placing a reference to the first view controller in the application’s delegate class where it can be accessed when needed within the PurchaseViewController instance. Select the AppDelegate.swift file and edit it to add this variable:

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?
    var homeViewController: ViewController?
.
.

Completing the ViewController Class

In order to complete the ViewController class implementation, select the ViewController.swift file and modify it to import the StoreKit framework. Also, code needs to be added to assign a reference to the view controller instance to the homeViewController variable previously added to the app delegate class so that it can be accessed from the PurchaseViewController:

import UIKit
import StoreKit

class ViewController: UIViewController {

    @IBOutlet weak var level2Button: UIButton!

    override func viewDidLoad() {
        super.viewDidLoad()
        let appdelegate = UIApplication.shared.delegate
                            as! AppDelegate
        appdelegate.homeViewController = self
    }
.
.
.

Finally, implement the enableLevel2 method which will be called by the PurchaseViewController instance to enable the Level 2 access button once the purchase is complete:

func enableLevel2() {
    level2Button.isEnabled = true
}

The ViewController class is now complete.

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

Completing the PurchaseViewController Class

The first steps in completing the PurchaseViewController class are to add some code to the viewDidLoad method. To begin with, until product information has been obtained and displayed to the user, the buy button should be disabled. The class also needs to be configured as the transaction observer for the purchase operation. Finally, a method needs to be called to obtain the product information for the purchase and display it to the user. To achieve these tasks, edit the PurchaseViewController.swift file and modify the viewDidLoad method accordingly:

override func viewDidLoad() {
    super.viewDidLoad()
    buyButton.isEnabled = false
    SKPaymentQueue.default().add(self)
    getProductInfo()
}

It will be the job of the getProductInfo method called from the viewDidLoad method above to contact the App Store and get product information for the specified ID and display it. The code for this method belongs in the PurchaseViewController.swift file and reads as follows:

func getProductInfo()
{
    if SKPaymentQueue.canMakePayments() {

        let request = SKProductsRequest(productIdentifiers: 
		NSSet(objects: self.productID) as! Set<String>)
        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:

func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {

    var products = response.products

    if (products.count != 0) {
        product = products[0]
        buyButton.isEnabled = true
        productTitle.text = product!.localizedTitle
        productDescription.text = product!.localizedDescription

    } else {
            productTitle.text = "Product not found"
    }

    let invalids = response.invalidProductIdentifiers

    for product in invalids
    {
         print("Product not found: \(product)")
    }
}

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

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 func buyProduct(_ sender: AnyObject) {
    let payment = SKPayment(product: product!)
    SKPaymentQueue.default().add(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 also needs to be implemented in PurchaseViewController.swift:

func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
    for transaction in transactions {

        switch transaction.transactionState {

        case SKPaymentTransactionState.purchased:
            self.unlockFeature()
            SKPaymentQueue.default().finishTransaction(transaction)

        case SKPaymentTransactionState.failed:
            SKPaymentQueue.default().finishTransaction(transaction)
        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.swift as follows:

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

func unlockFeature() {
    let appdelegate = UIApplication.shared.delegate
                                as! AppDelegate

    appdelegate.homeViewController!.enableLevel2()
    buyButton.isEnabled = false
    productTitle.text = "Item has been purchased"
}

This method obtains a reference to the home view controller stored in the application delegate and calls the enableLevel2 method of that view controller instance. The Buy button is then disabled and the text displayed on the product title label changed to indicate the successful purchase.

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, login using the sandbox account created in the previous chapter and wait for the purchase confirmation dialog to appear (Figure 109-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.


Ios 8 in app purchase confirm.png

Figure 109-4


When the purchase is complete Level 2 should now be accessible from the first scene.

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

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 Connect.

• 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 10 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 10 Application for In-App PurchasesConfiguring and Creating App Store Hosted Content for iOS 10 In-App Purchases