General Middleware

Implement In App Purchase for Consumables with XCODE

In order to take your apps to the next level, and make some money, you need to implement In App Purchase. There are different types of such purchase that can be found here. In this post, I will deal with the Consumables, that can be used once and they gets depleted upon use e.g. lives/gems in a game. Broadly below are the steps to implement In App Purchase:

1. Retrieve the products available in the App Store, and display it to the users in your app.

a) First step is to store your products, with the exact same name and products ID locally in the app or in an external database, as applicable. The simplest way is to maintain an String array in the app, containing all the product identifiers. But, a more professional way is to store the product details in a property list, and read information from there using below code.

guard let url = Bundle.main.url(forResource: "product_ids", withExtension: "plist") 
    else { 
           fatalError("Unable to resolve url for in the bundle.") 
         }
do {    
    let data = try Data(contentsOf: url)    
    let productIdentifiers = try PropertyListSerialization.propertyList(from: data, options: .mutableContainersAndLeaves, format: nil) as? [String]
   } catch let error as NSError {    
    print("\(error.localizedDescription)")
   }

Note that the name of the Property File will be product_ids.plist

From the above code, you get a local array of product identifiers. Make sure that the identifiers are exactly the same as mentioned in the AppStoreConnect

b) Query the App Store by creating a products request (SKProductsRequest) and initialize it with a list of your product identifiers. The class must be a delegate of SKProductsRequestDelegate, so that you can implement the call back, that is invoked upon retrieving the products from the AppStore.

// Keep a strong reference to the product 
request.var request: SKProductsRequest! 
func validate(productIdentifiers: [String]) {     
     let productIdentifiers = Set(productIdentifiers)      
     request = SKProductsRequest(productIdentifiers: productIdentifiers)     
     request.delegate = self      
     request.start() //This is where the app connects to the store to retieve all the products
}

Here is the SKProductsRequestDelegate callback:

var products = [SKProduct]()
// SKProductsRequestDelegate protocol method.           
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {                        if !response.products.isEmpty {       
products = response.products       
// Custom method.                
displayStore(products) //This is where you display the products to the user
}     
for invalidIdentifier in response.invalidProductIdentifiers {       
// Handle any invalid product identifiers as appropriate.    
}}

Keep a reference to the array of product objects (SKProduct) returned to the delegate since you will need the corresponding product object to create a payment request when the user purchases a product.

2. Requesting a Payment from the App Store

After you present your app’s store UI, users can make purchases from within your app. When the user chooses a product, your app creates and submits a payment request to the App Store.

a) Create a Payment Request

// Use the corresponding SKProduct object returned in the array from SKProductsRequest.         
let payment = SKMutablePayment(product: product)
payment.quantity = 2 //Mention this only if user has option to choose the product in multiple

b) Submit a Payment Request

Submit your payment request to the App Store by adding it to the payment queue.

SKPaymentQueue.default().add(payment)

3. Processing a Transaction

Register a transaction queue observer to get and handle transaction updates from the App Store. The App Store calls the transaction queue observer after it processes the payment request. The observer must conform to the SKPaymentTransactionObserver protocol. Always register a transaction queue observer as soon as your app is launched, as shown below. It should be ideally in the AppDelegate.swift. At the same time, remember to remove the observer as well upon app termination

import UIKit
import StoreKit


let iapObserver = StoreObserver.shared //Grab the Singleton Instance
class AppDelegate: UIResponder, UIApplicationDelegate {
                ....
    // Attach an observer to the payment queue.
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        SKPaymentQueue.default().add(iapObserver)
        return true
    }

    // Called when the application is about to terminate.
    func applicationWillTerminate(_ application: UIApplication) {
        // Remove the observer.
        SKPaymentQueue.default().remove(iapObserver)
    }
                ....
}

Implement the paymentQueue(_:updatedTransactions:) method on your transaction queue observer. StoreKit calls this method when the status of a transaction changes, such as when a payment request has been processed.

class StoreObserver: NSObject, SKPaymentTransactionObserver {
                ....
    //Initialize the store observer.
    override init() {
        super.init()
        //Other initialization here.
    }

    static let shared = StoreObserver() //Create the instance as a Singleton

    //Observe transaction updates.
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
     for transaction in transactions {
     switch transaction.transactionState {
          // Call the appropriate custom method for the transaction state.
       case .purchasing: showTransactionAsInProgress(transaction, deferred: false)
       case .deferred: showTransactionAsInProgress(transaction, deferred: true)
       case .failed: failedTransaction(transaction)
       case .purchased: completeTransaction(transaction)
       case .restored: restoreTransaction(transaction)
          // For debugging purposes.
       @unknown default: print("Unexpected transaction state \(transaction.transactionState)")
    }
     }
}
                ....
}

Create the Observer class as a singleton instance, to make sure that the same instance and its methods are called upon completion of the transaction.

Leave a Reply

Your email address will not be published. Required fields are marked *