首页 > 解决方案 > 存储在 CoreData 中的应用内购买回调函数

问题描述

我有一个ContentView使用EnvironmentObject. 应用内购买后,此对象应保存到CoreData. 我ContentView看起来像这样:

//ContentView
import SwiftUI

struct ContentView: View {
    @EnvironmentObject var modelData: ModelData
    @Environment(\.managedObjectContext) private var viewContext
    @StateObject var storeManager: StoreManager


    var body: some View {
        Button(action: {
            purchaseAndSaveToCoreData()
        }, label: {
            Text("buy for 100")
        })
    }
    
    func purchaseAndSaveToCoreData() {
        //Open the storeManager and do the purchase
        storeManager.purchaseProduct(product: storeManager.myProducts[0])
        
        //Wait for the purchase callback and then save an object from the EnvironmentObject modelData to CoreData here <- How?
        let newItemToStore = Item(context: viewContext)
        newItemToStore.name = modelData.name
        do {
            try viewContext.save()
        } catch {
            print(error.localizedDescription)
        }

    }
}

我的StoreManager课是这样的:

//StoreManager
import Foundation
import StoreKit

class StoreManager: NSObject, ObservableObject, SKProductsRequestDelegate, SKPaymentTransactionObserver {
    
    //FETCH PRODUCTS
    //[...] Fetching Process goes here
    
    
    //HANDLE TRANSACTIONS
    @Published var transactionState: SKPaymentTransactionState?
    
    func purchaseProduct(product: SKProduct) {
        if SKPaymentQueue.canMakePayments() {
            let payment = SKPayment(product: product)
            SKPaymentQueue.default().add(payment)
        } else {
            print("User can't make payment.")
        }
    }
    
    func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
        for transaction in transactions {
            switch transaction.transactionState {
            case .purchasing:
                transactionState = .purchasing
            case .purchased:
                UserDefaults.standard.setValue(true, forKey: transaction.payment.productIdentifier)
                queue.finishTransaction(transaction)
                transactionState = .purchased
                //Can I use my environmentObject modelData here?
            case .restored:
                UserDefaults.standard.setValue(true, forKey: transaction.payment.productIdentifier)
                queue.finishTransaction(transaction)
                transactionState = .restored
            case .failed, .deferred:
                print("Payment Queue Error: \(String(describing: transaction.error))")
                queue.finishTransaction(transaction)
                transactionState = .failed
            default:
                queue.finishTransaction(transaction)
            }
        }
    }
    
    func restoreProducts() {
        print("Restoring products ...")
        SKPaymentQueue.default().restoreCompletedTransactions()
    }
}

一切正常,购买发生。但我无法让关键的事情发挥作用。当它处于模式时,我如何在内部等待ContentView回调?我考虑过并想在这样的函数中使用它:paymentQueue.purchased@Published transactionStatepurchaseAndSaveToCoreData()

//ContentView
//[...]

storeManager.purchaseProduct(product: storeManager.myProducts[0])
        
//Wait for the purchase callback and then save an object from the EnvironmentObject modelData to CoreData here <- How?
if storeManager.transactionState == .purchased {
   let newItemToStore = Item(context: viewContext)
   newItemToStore.name = modelData.name
   do {
      try viewContext.save()
   } catch {
      print(error.localizedDescription)
   }
}

但是该if语句当然会在storeManager.purchaseProduct()函数之后立即运行并且不会触发。我知道寻找的正确时机是在StoreManagerat the内部case .purchased。但我不能EnvironmentObject在那里使用我的。我对 Swift 比较陌生,所以我可能在这里缺少关于回调、状态或触发函数的关键特性。

标签: swiftswiftuiin-app-purchase

解决方案


这个答案为我指明了正确的方向:当状态发生变化时,我如何运行一个动作?

我将我的按钮更改ContentView为:

Button(action: {
   purchaseAndSaveToCoreData()
}, label: {
   Text("buy for 100")
})
.onChange(of: storeManager.transactionState) { transactionState in
   if transactionState == .purchased {
      saveVirginKeyToCoreData()
   }
}

onChange属性监听 的状态变化transactionState


推荐阅读