Search code examples
swiftuiin-app-purchasestorekit

StoreKit problem while restoring In-App Purchase


I tried to find a tutorial about IAP, and I found : This one

But when I call function to restore, nothing happens. OnTap button I call : store.restorePurchase()

extension Store {
    func product(for identifier: String) -> SKProduct? { ...}
    func purchaseProduct (_ product: SKProduct){ ...}
    func restorePurchases() {
        SKPaymentQueue.default().restoreCompletedTransactions()
        print("restore")
    }
}

restore is print in my console. It is the only thing that is printed in console. But in the code above, you can see .restored:that should print "UserIsPremium"

extension Store : SKPaymentTransactionObserver {
    func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions : [SKPaymentTransaction]){
        for transaction in transactions {
            var shouldFinishTransaction = false
            switch transaction.transactionState{
            case .purchased, .restored:
                completedPurchases.append(transaction.payment.productIdentifier)
                print("UserIsPremium")
                shouldFinishTransaction = true
            case .failed:
                print("NotPremium")
                shouldFinishTransaction = true
            case .deferred, .purchasing:
                print("...")
                break
            @unknown default:
                print("unknown")
                break
            }
            if shouldFinishTransaction {
                print("shouldfinish")
                SKPaymentQueue.default().finishTransaction(transaction)
                DispatchQueue.main.async {
                    self.purchaseCompletionHandler?(transaction)
                    self.purchaseCompletionHandler = nil
                }
            }
        }
    }
}

My class Store :

import StoreKit

typealias FetchCompletionHandler = (([SKProduct]) -> Void)
typealias PurchaseCompletionHandler = ((SKPaymentTransaction?) -> Void)

class Store: NSObject, ObservableObject {

@Published var allFullVersion = [FullVersion]()

private let allProductsIdentifiers = Set ([
"ClemPapplis.OrientationEPS.versionFull"
])
private var completedPurchases = [String]() {
    didSet {
        DispatchQueue.main.async{ [weak self] in
            guard let self = self else { return }
            for index in self.allFullVersion.indices {
                if self.completedPurchases.contains(self.allFullVersion[index].id){
                    print("completedPurchases")
                    UserDefaults.standard.setValue(true, forKey: "Premium")
                }
                self.allFullVersion[index].isLocked =
                    !self.completedPurchases.contains(self.allFullVersion[index].id)
            }
        }
    }
}

I use sandbox testers. If I buy item, no problem, datas are print in console.

Did I forget something ?


Solution

  • It's been a few years since I used StoreKit, but I remember having an issue with restore purchases also (and yes, @ElTomato is correct, use a separate case - I'm surprised your code actually builds). I found that the following worked:

    public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
        for transaction:AnyObject in transactions {
            if let trans = transaction as? SKPaymentTransaction {
                switch trans.transactionState {
                case .purchased:
                    SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
                    delegate.purchaseComplete()
                case .failed:
                    SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
                case .restored:
                    SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
                default:
                    break
                }}}
    }
    func restorePurchases() {
        SKPaymentQueue.default().add(self)
        SKPaymentQueue.default().restoreCompletedTransactions()
    }
    public func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
        for _ in queue.transactions {
            alreadyPurchased = true
            delegate.purchasesRestored()
        }
        showAlert("Purchases have been restored.")
    }
    

    Don't worry about showAlert, that's just an extension I have for UIViewController. But what I'm not seeing in your code is paymentQueueRestoreCompletedTransactionsFinished(_ queue:).