Difference between revisions of "An iOS 8 Swift In-App Purchase Tutorial"
(New page: <table border="0" cellspacing="0" width="100%"> <tr> <td width="20%">Previous<td align="center">[[iOS 8 App Development Essentials|T...) |
(No difference)
|
Revision as of 13:34, 10 December 2014
Previous | Table of Contents | Next |
Preparing an iOS 8 Application for In-App Purchases | Configuring and Creating App Store Hosted Content for iOS 8 In-App Purchases |
<google>BUY_IOS8</google>
This chapter assumes that the steps outlined in the previous chapter (Preparing an iOS 8 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 97 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:
Figure 97-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 97 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 Horizontal Center in Container constraints for the three objects. Select the uppermost label and use the Pin 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 Center in 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.
The storyboard should appear as shown in Figure 97-2.
Figure 97-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 97 3:
[Image:ios_8_in_app_purchase_buy_scene.png]]
Figure 97-3
Ctrl-click on each of the three views so that all are selected and use the Auto Layout Align menu to add Horizontal Center in Container constraints to all of the views. Select the Label view and use the Pin menu to add a Spacing to nearest neighbor constraint on the top edge using the current value. Repeat this step for the Text View and Button view.
Select the Text View, display the Auto Layout Pin 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. 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? . .
<google>BUY_IOS8</google>
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.sharedApplication().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.enabled = true }
The ViewController class is now complete.
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.enabled = false SKPaymentQueue.defaultQueue().addTransactionObserver(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)) 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!, didReceiveResponse response: SKProductsResponse!) { var products = response.products if (products.count != 0) { product = products[0] as? SKProduct buyButton.enabled = true productTitle.text = product!.localizedTitle productDescription.text = product!.localizedDescription } else { productTitle.text = "Product not found" } products = response.invalidProductIdentifiers for product in products { println("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 func buyProduct(sender: AnyObject) { let payment = SKPayment(product: 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 also needs to be implemented in PurchaseViewController.swift:
func paymentQueue(queue: SKPaymentQueue!, updatedTransactions transactions: [AnyObject]!) { for transaction in transactions as [SKPaymentTransaction] { switch transaction.transactionState { case SKPaymentTransactionState.Purchased: self.unlockFeature() SKPaymentQueue.defaultQueue(). finishTransaction(transaction) case SKPaymentTransactionState.Failed: SKPaymentQueue.defaultQueue(). 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:
func unlockFeature() { let appdelegate = UIApplication.sharedApplication().delegate as AppDelegate appdelegate.homeViewController!.enableLevel2() buyButton.enabled = 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 and wait for the purchase confirmation dialog to appear (Figure 97 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.
Figure 97-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 Level 2 should now be accessible from the first scene.
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 8 application and provided some guidance in terms of troubleshooting tips in the event that in-app purchasing does not work.
<google>BUY_IOS8</google>
Previous | Table of Contents | Next |
Preparing an iOS 8 Application for In-App Purchases | Configuring and Creating App Store Hosted Content for iOS 8 In-App Purchases |