E. Huckabee E. Huckabee - 1 year ago 82
iOS Question

In App Purchases: Give User purchased item after purchase

this is kind of a two part question that ties into each-other and would not be worth asking seperately

First Problem

so i just finished a guide tutorial on youtube here about in-app purchases and he mostly covered the actual payment and restoration of bought items etc.

but the one thing he didnt cover that i actually needed was a way to actually give the Buyer (in my case in-game coins) the purchased item

here is the file with all the purchase functions and other things

import Foundation
import StoreKit

class IAPService: NSObject {

private override init() {}
static let shared = IAPService()

var products = [SKProduct]()
var paymentQueue = SKPaymentQueue.default()

func getProducts() {
let products: Set = [IAPProduct.StackOfCoins.rawValue,
IAPProduct.PileOfCoins.rawValue,
IAPProduct.BoxOfCoins.rawValue,
IAPProduct.MountainOfCoins.rawValue,
IAPProduct.ContainerOfCoins.rawValue]
let request = SKProductsRequest(productIdentifiers: products)
request.delegate = self
request.start()

paymentQueue.add(self)
}

func purchase(product: IAPProduct) {
guard let productToPurchase = products.filter({ $0.productIdentifier == product.rawValue }).first else { return }
let payment = SKPayment(product: productToPurchase)
paymentQueue.add(payment)


}
}
extension IAPService: SKProductsRequestDelegate {
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
self.products = response.products
print(response.products)
for product in response.products {
print(product.localizedDescription)
}
}
}
extension IAPService: SKPaymentTransactionObserver {
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction in transactions {
print(transaction.transactionState.status(), transaction.payment.productIdentifier)
switch transaction.transactionState {
case .purchasing: break
default: queue.finishTransaction(transaction)
}
}
}
}
extension SKPaymentTransactionState {
func status() -> String {
switch self {
case .deferred: return "deferred"
case .failed: return "failed"
case .purchased: return "purchased"
case .purchasing: return "purchasing"
case .restored: return "restored"
}
}
}


and here is the second file storing my products IDs

import Foundation
//crappy duck is the name of my game i am developing and these here represent increasing values of coins the user can buy

enum IAPProduct: String {
case StackOfCoins = "com.HucksCorp.Crappy-duck.StackOfCoins"
case PileOfCoins = "com.HucksCorp.Crappy-duck.PileOfCoins"
case BoxOfCoins = "com.HucksCorp.Crappy-duck.BoxOfCoins"
case MountainOfCoins = "com.HucksCorp.Crappy-duck.MountainOfCoins"
case ContainerOfCoins = "com.HucksCorp.Crappy-duck.ContainerOfCoins"

}


it works fine i can connect and access my products for sale (in-game coins) and all that but what i cannot figure out is how to change it so that it gives the user in-game coins based on the item he is buying

here is what im using to have the user actually buy stuff

this is in a collection view

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if indexPath.item == 0 {
IAPService.shared.pruchase(product: .StackOfCoins)
}
if indexPath.item == 1 {
IAPService.shared.purchase(product: .PileOfCoins)
}
if indexPath.item == 2 {
IAPService.shared.purchase(product: .BoxOfCoins)
}
if indexPath.item == 3 {
IAPService.shared.purchase(product: .MountainOfCoins)
}
if indexPath.item == 4 {
IAPService.shared.purchase(product: .ContainerOfCoins)
}
}


Second Problem

i have the value Coins stored in UserDefaults and someone else in one of my previous questions warned me that it was a bad idea to have a purchaseable product stored in UserDefaults

what is a second option to UserDefaults that i can access, add to, and subtract from with in-app purchases and with in-game purchaseable products

Summary

in case my questions werent clear (if they werent let me know so i can edit them)

my first question is how do i give the user the bought product after they purchased it

and my second question is another option to userDefaults value of Coins that is more secure then UserDefaults

Answer Source

Add another case to the switch in updatedTransactions:

switch transaction.transactionState {
    case .purchased:
        let coinsValue = generateValue(transaction.payment.productIdentifier)
        //add a call to your function here
        break;
    case .purchasing: break
    default: queue.finishTransaction(transaction)
}

And call your method of adding coins in that case, with respect to the product that was bought.

Here is a method to get int from the identifier:

private func generateValue(_ identifier: String) -> Int
{
    switch identifier {
        case "com.HucksCorp.Crappy-duck.StackOfCoins":
            return 1
        case "com.HucksCorp.Crappy-duck.PileOfCoins":
            return 2
        /// etc..
        default:
            return 0
    }
}

Regarding coins storage, you can sync them to a file, store in keychain or use server side storage, which probably is the best option.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download