应用内购买回调函数存储在 CoreData 中
In-App Purchase callback function to store in CoreData
我有一个使用 EnvironmentObject
的 ContentView
。应用内购买后,该对象应保存到 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
class是这样的:
//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
处于 .purchased
模式时,我如何在 ContentView
中等待 paymentQueue
的回调?
我考虑了 @Published transactionState
并想在 purchaseAndSaveToCoreData()
函数中使用它,如下所示:
//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()
函数之后立即运行并且不会触发。我知道寻找的正确时机是在 case .purchased
处的 StoreManager
内。但是我不能在那里使用我的EnvironmentObject
。我对 Swift 比较陌生,所以我可能在这里遗漏了有关回调、状态或触发函数的关键特性。
这个答案为我指明了正确的方向:
我将 ContentView
中的按钮更改为:
Button(action: {
purchaseAndSaveToCoreData()
}, label: {
Text("buy for 100")
})
.onChange(of: storeManager.transactionState) { transactionState in
if transactionState == .purchased {
saveVirginKeyToCoreData()
}
}
onChange
属性监听transactionState
的状态变化。
我有一个使用 EnvironmentObject
的 ContentView
。应用内购买后,该对象应保存到 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
class是这样的:
//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
处于 .purchased
模式时,我如何在 ContentView
中等待 paymentQueue
的回调?
我考虑了 @Published transactionState
并想在 purchaseAndSaveToCoreData()
函数中使用它,如下所示:
//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()
函数之后立即运行并且不会触发。我知道寻找的正确时机是在 case .purchased
处的 StoreManager
内。但是我不能在那里使用我的EnvironmentObject
。我对 Swift 比较陌生,所以我可能在这里遗漏了有关回调、状态或触发函数的关键特性。
这个答案为我指明了正确的方向:
我将 ContentView
中的按钮更改为:
Button(action: {
purchaseAndSaveToCoreData()
}, label: {
Text("buy for 100")
})
.onChange(of: storeManager.transactionState) { transactionState in
if transactionState == .purchased {
saveVirginKeyToCoreData()
}
}
onChange
属性监听transactionState
的状态变化。